|May the source be with you, but remember the KISS principle ;-)|
|Contents||Bulletin||Scripting in shell and Perl||Network troubleshooting||History||Humor|
|News||X Window System||Softpanorama Bookshelf||Recommended Links||Activating the gnome vino from the command line||Configuration|
|Exporting_display||"Can't open display" Error||.Xresources||xrdb||Using xauth||Xdefaults|
|Fonts in X||X11 security||X display manager||XDMCP||vnc||vino|
|Installing X11 and Gnome Desktop in RHEL||Activating the gnome VNC vino-server from the command line||VNC on Linux||VNC on Windows||Cygwin/X||Xming|
|Troubleshooting||Diagnosing problems with remote X11 sessions via SSH||Too high refresh rate/ too high resolution problem||Tips||Humor||Etc|
Vino is the default VNC server in Gnome which provides the capability to view Gnome desktop via VNC client.
It can be started manually using the following script (IP of remote host is passed as parameter):
#!/bin/bash export DISPLAY=:0.0 xhost $1 if [ ! `netstat -nl | grep 590[0-9]` ] ; then /usr/libexec/vino-server & fi
Vino can be configured from command line if you do not have access to GUI console: see Activating the gnome vino from the command line
To configure vino from within GNOME, go to System > Preferences > Remote Desktop
To set vino to request access each time, tick Allow other users to view your desktop in the Remote Desktop configuration window
To set a password, tick Require the user to enter this password:
To put vino in view-only mode, untick Allow other users to control your desktop
To only allow local connections: click on the tab marked Advanced, then tick Only allow local connections
To allow connections from anywhere: click on the tab marked Advanced, then untick Only allow local connections
See also VNC-Servers - Community Ubuntu Documentation
The host side is to be implemented as a VNC server using the libvncserver library. The VNC server will act as an X client and poll the local X display for the contents of the framebuffer and notify the VNC clients if there have been any changes. Input events coming from the clients will be injected into the X display using the XTEST, extension.
The VNC client we will most likely be a modified version of an existing Java client. The advantage of having a Java client is that it may be used to connect to the host from any platform.
7.2. Monitoring The Local Display
To implement a VNC server you need to know the contents of the local framebuffer in order to pass this information onto the VNC clients.
Currently, as an X client, there is only one way to do this and that is by doing a GetImage on the root window which basically copies the entire framebuffer from the X server to the X client. The main problem with this approach is that without knowing what parts of the framebuffer has actually changed since the last time you updated, you are wasting an enormous amount of resources copying the entire framebuffer each time.
There are a number of possiblities to lessen the inefficiency here. The first is to limit the amount of polling you do per update of the framebuffer. For example, every update you could just check a certain number of scanlines against your local copy of the frame buffer and if parts of the scanline differ, then do a GetImage on a number of tiles which capture those changes to the scanline. This is the approach taken by krfb and x0rfbserver.
Another possibility is an X extension to notify the X client of changes to the framebuffer, thereby negating the need for continually polling the X server. When the client receives the notification it can do a GetImage to update its copy of the framebuffer with the latest changes. Keith Packard is currently working on an extension to do exactly this called XDAMAGE.
Initially we will use the x0rfbserver approach of polling the screen, but will later implement support for the XDAMAGE extension when it becomes available.
7.3. Input Event Handling
The VNC server will need to handle two types of input events coming from VNC clients - keyboard and pointer events. These events will be injected into the Xserver using the XTEST extension.
To inject a keyboard event into the X server you invoke XTestFakeKeyEvent with the appropriate keycode. The X server then maps this keycode, according to the current modifier state, to a keysym. We need to make sure that they keycode we pass to the X server maps to the same keysym as the keysym we received from the VNC client. We can reverse map a keysym to a keycode, but we also need to make sure the modifier state is such that the keycode will map back to that keycode. Both krfb and x11vnc use the same code for ensuring this and we can just copy that.
You can inject into the X server button presses/releases using XTestFakeButtonEvent() and pointer movement using XTestFakeMotionEvent(). The PointerEvent you receive from the client contains information on the state of each button and the current pointer location. The only slightly difficult part here is translating the button state information to button presses/releases, but it merely involves keeping track of the previous button state.
7.4. Cursor Handling
The basic problem here is how to allow the VNC client to see the cursor. There are several possible approaches:
- Draw the cursor directly to the VNC server's copy of the framebuffer and send framebuffer updates as the cursor is moved around. The client will see the cursor being moved in all cases.
- Provide the cursor image to the client using the RichCursor or XCursor psuedo encoding and let the client draw the cursor locally. The client only sees cursor movement when that client is the one moving the cursor.
- Again provide the cursor image to the client, but also send the client updates on the pointer position using the PointerPos pseudo-encoding which the client can use to update the position of its locally drawn cursor. Again, the client will see the cursor movement no matter who is moving it.
Approach (2) isn't very useful - the client needs to be able to see cursor movement on the host. For that reason, we will not advertise the cursor image to the client unless it supports *both* cursor position and cursor shape updates. If the client only supports one or the other, we ignore the support for that encoding and always draw the cursor to the framebuffer.
One problem here is that, in the screen polling case, because we will be comparing the local copy of the framebuffer (which may, with approach (1) above, have the cursor drawn in it) to the actual framebuffer (which will not contain the cursor image) we will need to undraw the cursor before doing any comparison. Instead of complicating the screen polling code with this detail we will draw the cursor image to the framebuffer just before sending a frame buffer update to the client and then immediately undraw it.
Of course, with either approach (1) or (2) we need some mechanism by which we can determine the current cursor position and shape. The only way to determine the current cursor position is by regularily polling using XQueryPointer(). Determining the current cursor shape is not possible but such support is to be added to the XFIXES extension. [FIXME: more details on this].
7.5. libvncserver Work
libvncserver contains lots of code from different VNC server implementations. The intent is to bring all that code together under one API which makes it easy to write VNC servers. However, rather than being a library, it seems more like a full VNC server implementation around which you can wrap a main function.
There are a number of problems with the library which can be fixed in a fairly straightforward manner, by extending the API slightly and cleaning bits up.
Other concerns around the library containing way more implementation that we would like/need, many private functions exposed in the API, structures that will likely need to be expanded being exposed in the API and a general feeling that the library cannot hope to maintain ABI compatibility are much harder to address. We have the option of just statically linking to the library, and so, the project will not be held up by these problems, but we should continue to consider coming up with a plan to fix these problems.
Initially, the project will contain a copy of libvncserver with the following changes:
- API to allow glib mainloop integration.
- Support for version 3.7 of the RFB protocol - this adds security type negotiation.
- A hook so that we can allow the user to decide whether or not an authenticated client should be allowed to connect.
- Re-worked cursor handling so that the cursor remains drawn to the server's copy of the framebuffer only for the duration of rfbSendFrameBufferUpdate().
- API to set the current cursor postion.
- Only send cursor shape updates when the client also has support for cursor position updates and vice versa.
- Removed default implementations of hooks and make that the default action when the hook isn't implemented.
- TRUE == 1 instead of -1 to match glib.
- Warnings fixes.
- LIBVNCSERVER prefix removed from the #ifdefs so that we can use a normal config.h. libvncserver really shouldn't have platform dependant defines in its header anyway.
- Threading support disabled but not removed.
- Lots of code which we don't need removed:
- UDP support
- Vestiges of CORBA support
- Back channel support
- setTranslateFunction(), getCursorPtr and displayHook hooks
- HTTP server implementation
- Support for drawing GUIs to the framebuffer on the server side.
- Command line argument processing.
7.6. Service Discovery
In order to implement the ability to browse the network for available remote desktop servers there must be some way to enumerate the available servers. One possible mechanism for doing this is DNS Based Service Discovery, a draft of which is currently on the IETF standards track.
DNS-SD is a convention for naming and structuring DNS resource records such that a client can query the DNS for a list of named instances of a particular service. The client can then resolve one of these named instances to an address and port number via a SRV record.
In the remote desktop case, a client could query the DNS for PTR records of _rfb._tcp.<domain> and would be returned a list of named instances of RFB servers, using TCP, on the domain. For example:PTR:_rfb._tcp.ireland.sun.com -> SRV:Mark's Desktop._rfb._tcp.ireland.sun.com SRV:Gman's Desktop._rfb._tcp.ireland.sun.com
(Note the way the Service Instance Name is a user-friendly name containing arbitrary UTF-8 encoded text. It is not a host name.)
The client would then display the list of available remote desktop servers - i.e. "Mark's Desktop" and "Gman's Desktop" - and allow the user to choose one. If the user chooses "Mark's Desktop" the client can then resolve that SRV record associated with the remote desktop instance.SRV:Mark's Desktop._rfb._tcp.ireland.sun.com -> markmc-box.ireland.sun.com:5900
The client can then resolve the "markmc-box.ireland.sun.com" hostname and using the resulting ip address connect to the remote desktop server on port 5900.
While DNS-SD seems like a perfect mechanism by which remote desktop instances may be queried for, there remains the problem of how the DNS is populated with the details of these services to begin with.
A related draft proposal on the IETF standards track is Multicast DNS. The idea behind Multicast DNS is to allow a group of hosts on a local link, in the absence of a convetionally managed DNS server, to co-operatively manage a collection of DNS records and allow clients on that same local link query those records.
The scheme works by each client connecting to the mDNS multicast IPv4 address and sending/receiving DNS-like queries/answers to port 5353. Between them, the clients manage the top-level ".local." domain and negotiate any conflicts that arise. So, for example, the host referenced by "markmc-box.ireland.sun.com" in the above example could also be resolved using the host name "markmc-box.local" by other Multicast DNS clients on the same link.
In order to be queriable by Multicast DNS, our remote desktop server could act as a Multicast DNS Responder and Querier and register the remote desktop service there. Here's how the example above would look like if we were using mDNS:
Client queries the local link for remote desktop servers ...PTR:_rfb._tcp.local
... and receives a reply first from markmc-box ...-> SRV:Mark's Desktop._rfb._tcp.local
... and then a reply from gman-box:-> SRV:Gman's Desktop._rfb._tcp.local
Once the user has selected "Mark's Desktop" from the displayed list, the client resolves that service and receives a reply once again from markmc-box:SRV:Mark's Desktop._rfb._tcp.ireland.sun.com -> markmc-box.local:5900
The client then resolves "markmc-box.local" to an ip address (still using Multicast DNS) and connects to that address on port 5900.
Luckily, implementing this won't require writing an mDNS implementation from scratch. There is an existing implementation in GNOME CVS which integrates nicely with glib's main loop and there are plans to centralise this in a desktop service advertisement and discovery daemon.
Another possible mechanism for making remote desktop service information available via DNS is to use Dynamic DNS Updates add DNS-SD records to a conventional DNS server. However, the majority of DNS server deployments restrict (for obvious security reasons) the ability to update DNS records completely or to only a few known hosts. Because using this mechanism would require installation sites to change their DNS administration policies, this is obviously not an attractive option.
7.7. Security Considerations
7.7.1. VNC Authentication
VNC uses a simple DES based challenge-response authentication scheme. In order to authenticate the client, the server sends a random 16 byte challenge and the client then encrypts the challenge with DES using the user supplied password as a key. If the response matches the expected result, the client is authenticated. Otherwise, the server closes the connection. There are a number of possible vulnerabilities with this mechanism.
Firstly, the password, being limited to 8 characters, could be brute force guessed by an attacker who continually tries to authenticate using different passwords. The standard way of making such attacks unfeasible is to enforce a delay between failed authentication attempts - i.e. if there has been a failed authentication attempt, delay sending the challenge to the next client who connects for a number of seconds.
Another possible vulnerability is the predictability of the random challenge sent by the server. If the server, under any circumstances, sends a challenge which has previously been used in a successful authentication attempt there is the possibility that an attacker may use the previously observered valid response again. An example of such is if the server re-seeds the random number generator used to produce the challenge with the current time on each connection attempt. In this case, if an attacker connects to the VNC server within the same one second window as a valid client, then the attacker will receive the same challenge as the valid client and use the response from that client to authenticate. To avoid such a vulnerability the server should produce highly unpredictable challenges using the cryptographically strong random number generator providied with the GNU TLS library.
Challenge-response authentication schemes are inherently susceptible to man-in-the-middle attacks. The basic idea is that attacker uses a client to generate a valid response for a given challenge. One way of carrying out such an attack is if the attack can intercept and modify the packets flowing between the client and the server. The attacker can then replace the challenge from the server with a challenge the attacker has received in a pending authentication attempt. The client then returns a valid response for that challenge with which the attacker can use to complete its authentication.
Given that this tool is aimed mainly at system administrators administering a network of many desktop machines, and given that an administrator would likely set the same password for the remote desktop server on each of these machines, a more worrying man-in-the-middle attack is:
("C" is the administrator using a VNC client, "S" is the VNC server under attack and "M" is the attacker.)
- M starts a modified VNC server which advertises itself on the local link using mDNS and DNS-SD (see the "Service Discovery" above)
- C connects to M's modified VNC server by selecting it from the list of available VNC servers
- M then connects to S and receives a challenge
- M sends this challenge to C
- C sends back a valid response to M
- M sends a failed authentication message to C.
- M uses this response to authenticate against S and is granted access.
There is no way to protect VNC's challenge response authentication mechanism from such an attack.
DES, by today's standards, is quite a weak encryption mechanism. Given that in this case that both plaintext and ciphertext (the challenge and response) are both available a brute force attack to find the key (the password in the VNC case) is possible. Brute force cracking of DES is a much discussed. A large amount of computing power would be required for such an attack and given that this tool would only deployed on private networks, it is perhaps not an immediate concern. However, in the years to come it is to be expected that such attacks would beome much more common and easy to perform.
RFB protocol messages are sent across the network unencrypted. This is an obvious security concern because an attacker may snoop the protocol packets and, using a modified VNC viewer, observe a VNC session in progress. Even more worrying, is that all key presses are sent in the open and may be snooped. Considering that system administrators are the primary target audience and that they are likely to enter the root password when running some system utility, the password could be snooped and used to gain root-level access to the machine.
In order to protect the VNC session from such attacks, the protocol should be extended to allow the stream to be encrypted. Luckily, the RFB protocol was designed to allow such extensions while maintaining compatibility.
The encryption of the RFB stream will be implemented with TLS/SSL using the gnutls library and, for the Java client, the Java Secure Socket Extension (JSSE).
TLS is a protocol designed to provide privacy, data integrity, compression and, optionally, peer authentication using public key cryptography. The protocol mainly consists of two parts - the Record Protocol and the Handshake Protocol. The Record Protocol is responsible for fragmenting, compressing, hashing and encrypting the data to be transmitted. The Handshake Protocol involves the peers agreeing on a protocol version, cipher suite and compression method, generating a shared secret and, optionally, exchanging certificates to allow the peers to authenticate one another (either or both peers may be authenticated).
New security types will be added (see below) which will cause the client and server to begin the TLS handshaking protocol immediately after one of those security types has been agreed upon. If VNC authentication is required, that challenge-response exchange will happen immediately after the TLS handshake has completed.
The peer authentication which may take place as part of the TLS handshake involves the peers exchanging certificates (currently only X.509 certificates are supported by the protocol but support for OpenPGP certificates has been proposed) and verifying their identity. In order to support server certificate authentication the VNC client will need have some sort of certificate store which contains the server certificates the client trusts - this is useful because it prevents a man-in-the middle attack. To support client certificate authentication, the VNC server will also require a certificate store listing the clients who are authorised to connect - this is useful because the password is no longer a weak point, but also that it would be generally more convenient for a system administrator to distribute his certicate to each of the desktop systems he administers and never have to type in a password.
If certificate based peer authentication is not used the client and server agree on a secret using anonymous Diffie-Hellman key exchange.
TLS supports compression of the communication stream. Some investigation should be carried out to see if using this compression mechanism is with uncompressed RFB tiles results in better bandwidth usage than no TLS compression and compressed RFB tiles.
7.7.3. New RFB Security Types
The negotiated security-type in the RFB protocol is an 8 bit unsigned integer. Currently there are only two possible values: "None"(1) to indicate no authentication is needed and "VNC authentication"(2) to indicate that the client is to be authenticated using the challenge-response scheme detailed above. 0 indicates and error condition.
We will add a further four security types:
- Anonymous TLS (3)
- TLS With VNC Authentication (4)
- TLS With Server Certificate and VNC Authentication (5)
- TLS With Server and Client Certificate Authentication (6)
In order to ensure interoperability with other implementations, these security types must be registered with RealVNC who maintain the RFB protocol specification.
7.7.4. Security Related Preferences
A number of preferences will be provided which will have a direct impact of the security of the system. Their meaning and rationale for their existance is detailed below:
- /desktop/gnome/remote_access/enabled (boolean, default=false)
If false, the Remote Desktop server will not be started at login time and if running will not allow any new remote connections, put all existing clients on hold and exit after a 30 second timeout.
The rationale for the preference is that unless Remote Desktop access is to actually be used, it is much more preferable to not have the server running for both resource consumption and security reasons.
The default for the preference is false under the assumption that Remote Desktop access will not be used by the majority of the user base.
- /desktop/gnome/remote_access/prompt_enabled (boolean, default=true)
If true, once a client has connected and been authenticated the user is prompted on whether the client connection should be allowed to be completed.
The rationale here is that in the majority of scenarios, the user on the host machine should be in control of whether or not remote users are allowed to connect to their desktop. Even if the host user has enabled remote access, set a password and informed some other user of that password, the host user may still want to both be aware of another user connecting and also decide whether that particular time is suitable for the remote user to connect.
The default is true for two reasons. Firstly, it is assumed that most users will want to vet new connections and, secondly, because by default there is no authentication required to connect this prompt will provide some level of manual authentication.
- /desktop/gnome/remote_access/view_only (boolean, default=false)
If true, remote users will only be allowed to view the remote desktop and all keyboard, pointer and clipboard events from the remote user will be ignored.
The view only preference is intended to support the simple collaboration scenario where a number of remote users will connect to a single host to observe something happening on the host machine. In this scenario the host will no want any of the remote users to use the pointer or keyboard on the host.
The default is false because this is not the primary scenario we are expecting to target.
- /desktop/gnome/remote_access/require_encryption (bool, default=true)
If true, the host requires that all connecting client use TLS encryption. Any clients attempting to use the "None" and "VNC Authentication" security types will be refused. This will only have affect the cases where the VNC viewer being used by remote user does not have support for TLS encryption. If the viewer does have support, it will always be used.
The preferences is provided so that the host may make the policy decision on whether unencrypted connections should be allowed.
However, in most cases it is expected that the host will require that only encrypted connections be allowed so as to not allow any information on the host to be compromised. For this reason, the default value for the preference is true.
- /desktop/gnome/remote_access/authentication_methods (list, default=[none])
The list of authentication which the server will advertise. Currently the supported values are "none" and "vnc", but when certificate based authentication is implemented "server-cert-with-vnc" and "server-cert-with-client-cert" will also be supported.
The preference is provided so as to allow the host user decide on how remote users should be authenticated. The host may decide that no authentication is required, that password or certificate based authentication should be used.
The default value is "none" because there is no point in having the default value be "vnc" because no password would be set.
- /desktop/gnome/remote_access/vnc_password (base64 encoded string, default=<unset>)
The password used to authenticate the remote user when VNC authentication is being used. The password is stored in GConf base64 encoded to provide an extra level of secrecy. However, the secrecy of the password is guaranteed by the fact that the files which GConf stores preference values in are only readable by the user in question.
7.7.5. Summary of Security Considerations
How to put this ? There must be some standard methodology to lay out the specific types of attacks you are and are not protecting against with different configurations e.g. given the following configuration:
- allowed = true
- require_encryption = true
- authentication_methods = [vnc]
- vnc_password = strong-password
and based on the following assumptions about potential attackers:
- Has the ability to snoop the intervening network stream.
- Does not have the ability to modify the intervening network stream.
- Does not have the ability to brute force crack the DES key given the challenge and response.
- Does not have the ability to spoof an IP address.
- Does not have access to the host machine as either root or the user who is running the server.
- Does not have any prior knowledge of what the password may be.
- Does not have access to the user's X display.
- Does not have access to the user's GConf daemon.
then no attacker should be able to:
- Snoop the host's screen contents from the VNC session.
- Snoop keyboard/pointer/clipboard events from the client.
- Obtain access to the VNC session.
- Obtain shell access to the host machine as root or the user who is running the server.
But you are also making assumptions about the behaviour of the user on both sides - e.g. that the remote user has the correct IP address and port number for the host she wishes to connect to, and not the IP address and port number of an attacker.
What problems remain ?
FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available in our efforts to advance understanding of environmental, political, human rights, economic, democracy, scientific, and social justice issues, etc. We believe this constitutes a 'fair use' of any such copyrighted material as provided for in section 107 of the US Copyright Law. In accordance with Title 17 U.S.C. Section 107, the material on this site is distributed without profit exclusivly for research and educational purposes. If you wish to use copyrighted material from this site for purposes of your own that go beyond 'fair use', you must obtain permission from the copyright owner.
ABUSE: IPs or network segments from which we detect a stream of probes might be blocked for no less then 90 days. Multiple types of probes increase this period.
Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers : Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism : The Iron Law of Oligarchy : Libertarian Philosophy
War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda : SE quotes : Language Design and Programming Quotes : Random IT-related quotes : Somerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose Bierce : Bernard Shaw : Mark Twain Quotes
Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 : Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law
Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds : Larry Wall : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOS : Programming Languages History : PL/1 : Simula 67 : C : History of GCC development : Scripting Languages : Perl history : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history
The Peter Principle : Parkinson Law : 1984 : The Mythical Man-Month : How to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Haterís Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite
Most popular humor pages:
Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor
The Last but not Least
Copyright © 1996-2016 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License.
Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.
FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available to advance understanding of computer science, IT technology, economic, scientific, and social issues. We believe this constitutes a 'fair use' of any such copyrighted material as provided by section 107 of the US Copyright Law according to which such material can be distributed without profit exclusively for research and educational purposes.
This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...
|You can use PayPal to make a contribution, supporting development of this site and speed up access. In case softpanorama.org is down you can use the at softpanorama.info|
The statements, views and opinions presented on this web page are those of the author (or referenced source) and are not endorsed by, nor do they necessarily reflect, the opinions of the author present and former employers, SDNP or any other organization the author may be associated with. We do not warrant the correctness of the information provided or its fitness for any purpose.
Last modified: September 12, 2017