At Browserling I just solved an interesting Windows sysadmin problem of how to allow Windows users to run just a few programs and nothing else. Since anyone can use Browserling for free, I had to find a way to restrict them from downloading and running viruses and trojans. So I took time to investigate and found a very elegant solution. No one had really documented this so this blog post will be very handy for people who can't figure it out themselves.

The solution is to configure the Software Restriction Policy (SRP) in the user's Group Policy Object (GPO) and disallow the user to run everything except the programs that are necessary to login and the programs you want the user to use.

The hardest part was figuring out all the programs that the user absolutely must be allowed to run for him/her to login the system:

  • C:\Windows\explorer.exe
  • C:\Windows\System32\csrss.exe
  • C:\Windows\System32\dwm.exe
  • C:\Windows\System32\rdclip.exe
  • C:\Windows\System32\taskhost.exe
  • C:\Windows\System32\TSTheme.exe
  • C:\Windows\System32\userinit.exe

If you don't have these in the SRP, then the user will never be able to login. So make sure they are in the SRP's "Additional Rules" and their security level is "Unrestricted."

After you add these, the user will be able to login but he wont be able to run anything! He'll just see desktop and that's it. If you want to allow the user to run, let's say, Paint, then add C:\Windows\System32\paint.exe the Unrestricted Access list.

Here is a precise list of steps to take to disallow running programs. Run them from Administrator account:

1. Open up the Microsoft Management Console (Start -> Run -> mmc):

2. Select File -> Add/Remove Snap-in.

3. Select Group Policy Object.

4. Click Add.

5. Click Browse, select the user you want to configure the GPO for.

6. Click Finish, and OK. Now you'll see the tree view with "<username> Policy."

7. Navigate to User Configuration -> Windows Settings -> Security Settings -> Software Restriction Policies.

8. Right click on Software Restriction Policies and click New Software Restriction Policies.

9. Two new items will appear the tree. Click the Security Levels.

10. Double click the Disallowed security level and click "Set as Default."

11. Go to the other item, the Additional Rules.

12. Delete the crap that Windows adds there by default (%HKEY_LOCAL_MACHINE\... something).

13. Add the rules that I documented in the beginning of the article. They are absolutely necessary for the user to login into the machine (either via desktop or remote desktop).

14. Add full paths to programs that you wish to allow the user to run (such as C:\windows\system32\paint.exe, ... etc.). Make sure the users can't overwrite the programs with their own, otherwise they might be able to execute their own programs.

15. Save the GPO.

16. Done!

Thanks to lewellyn for helping out with all this!

Ps. As I like to joke, the new title of this blog soon will be "Peteris Krumins's blog on Windows Administration" as I have spent so much time during past month messing with Windows. ;)

This is going to be a small privacy tutorial on how to clear cache, cookies and history in all five major browsers - Internet Explorer, Chrome, Firefox, Opera and Safari. The techniques here work only on Windows operating system but it's not hard to transfer the techniques to other systems. The tutorial comes with Windows batch scripts for each browser. It also documents how to erase the nasty flash cookies that are browser independent.

I wrote these batch scripts for the Browserling startup that I am doing together with James Halliday. The problem was that the browsers had to be reset between consequent uses. The easiest way to solve it was to run a batch cleanup script after each browser.

Google Chrome

Chrome stores history, cookies, cache and bookmarks in various databases and directories in the per-user application data directory at C:\Users\<username>\AppData\Local\Google\Chrome\User Data. The easiest way to get rid of all this data is just to erase everything there. Chrome creates this directory anew if it finds it missing.

@echo off

set ChromeDir=C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data

del /q /s /f "%ChromeDir%"
rd /s /q "%ChromeDir%"

This simple batch script first deletes all files in %ChromeDir% directory via del command and then deletes the directory itself via rd command. The /q flag makes the del command quiet, the /s makes it delete files from all subdirectories, and /f forces it to delete read-only files, too. The /s flag to rd makes it delete all subdirectories and /q makes rd quiet.

Mozilla Firefox

Firefox stores cookies, cache and history in two places. The first is per-user appdata directory C:\Users\<username>\AppData\Local\Mozilla\Firefox\Profiles and the second place is roaming profile data directory C:\Users\<username>\AppData\Roaming\Mozilla\Firefox\Profiles. To get rid of all the private data, delete the local data directory and delete all sqlite databases from the roaming data directory.

@echo off

set DataDir=C:\Users\%USERNAME%\AppData\Local\Mozilla\Firefox\Profiles

del /q /s /f "%DataDir%"
rd /s /q "%DataDir%"

for /d %%x in (C:\Users\%USERNAME%\AppData\Roaming\Mozilla\Firefox\Profiles\*) do del /q /s /f %%x\*sqlite

The for command loops over all profile directories and deletes all the sqlite databases. You can also delete the whole roaming data directory but I didn't because Firefox stores extensions there, and there are several I use for Browserling.

Opera

Opera also stores cookies, cache and history in two different locations - the user's application data directory C:\Users\<username>\AppData\Local\Opera\Opera and the user's roaming data directory C:\Users\<username>\AppData\Roaming\Opera\Opera. Just get rid of both directories and you're safe.

@echo off

set DataDir=C:\Users\%USERNAME%\AppData\Local\Opera\Opera
set DataDir2=C:\Users\%USERNAME%\AppData\Roaming\Opera\Opera

del /q /s /f "%DataDir%"
rd /s /q "%DataDir%"

del /q /s /f "%DataDir2%"
rd /s /q "%DataDir2%"

Apple Safari

Safari also stores cookies, cache and history in two locations - the user's application data directory C:\Users\<username>\AppData\Local\Apple Computer\Safari and user's roaming data directory C:\Users\<username>\AppData\Roaming\Apple Computer\Safari

@echo off

set DataDir=C:\Users\%USERNAME%\AppData\Local\Applec~1\Safari
set DataDir2=C:\Users\%USERNAME%\AppData\Roaming\Applec~1\Safari

del /q /s /f "%DataDir%\History"
rd /s /q "%DataDir%\History"

del /q /s /f "%DataDir%\Cache.db"
del /q /s /f "%DataDir%\WebpageIcons.db"

del /q /s /f "%DataDir2%"
rd /s /q "%DataDir2%"

Microsoft Internet Explorer

Internet Explorer is much more tricker. It stores history, cookies and cache all over the place, including registry. Here is the batch script that deletes all that data from all the places:

@echo off

set DataDir=C:\Users\%USERNAME%\AppData\Local\Microsoft\Intern~1

del /q /s /f "%DataDir%"
rd /s /q "%DataDir%"

set History=C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\History

del /q /s /f "%History%"
rd /s /q "%History%"

set IETemp=C:\Users\%USERNAME%\AppData\Local\Microsoft\Windows\Tempor~1

del /q /s /f "%IETemp%"
rd /s /q "%IETemp%"

set Cookies=C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Windows\Cookies

del /q /s /f "%Cookies%"
rd /s /q "%Cookies%"

C:\bin\regdelete.exe HKEY_CURRENT_USER "Software\Microsoft\Internet Explorer\TypedURLs"

Notice that the last command is regdelete.exe. It's a small win32 utility that I wrote in c++ that erases the IE history, because it stores it in registry.

Here is the regdelete.c program:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
// compile as: mingw32-g++ regdelete.c -o regdelete.exe -mwindows

#define eq(s1,s2) (strcmp((s1),(s2))==0)

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) 
{ 
    if (!cmdLine || !strlen(cmdLine)) {
        printf("Usage: regdel.exe <HKEY> <path to regkey> - be careful not to delete whole registry\n");
        return 1;
    }

    int argc;
    LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);

    if (argc < 3) {
        printf("Usage: regdel.exe <HKEY> <path to regkey> - be careful not to delete whole registry\n");
        return 1;
    }

    char **argv8 = (char **)malloc(sizeof(char *) * argc);
    for (int i = 0; i<argc; i++) {
        int len = wcslen(argv[i]);
        argv8[i] = (char *)malloc(sizeof(char)*(len+1));
        wcstombs(argv8[i], argv[i], len+1);
    }

    HKEY hkey;
    if (eq(argv8[1], "HKEY_CLASSES_ROOT")) {
        hkey == HKEY_CLASSES_ROOT;
    }
    else if (eq(argv8[1], "HKEY_CURRENT_CONFIG")) {
        hkey = HKEY_CURRENT_CONFIG;
    }
    else if (eq(argv8[1], "HKEY_CURRENT_USER")) {
        hkey = HKEY_CURRENT_USER;
    }
    else if (eq(argv8[1], "HKEY_LOCAL_MACHINE")) {
        hkey = HKEY_LOCAL_MACHINE;
    }
    else if (eq(argv8[1], "HKEY_USERS")) {
        hkey = HKEY_USERS;
    }
    else {
        printf("Unknown hkey\n");
        return 1;
    }

    HKEY key;
    int status = RegOpenKeyEx(hkey, argv8[2], 0, KEY_ALL_ACCESS, &key);
    if (status != ERROR_SUCCESS) {
        printf("failed opening %s\n", argv8[2]);
        return 1;
    }

    std::vector<std::string> vals;

    for (unsigned int i = 0; ; i++) {
        DWORD size = 1024;
        char val[size+1];
        DWORD type;
        status = RegEnumValue(key, i, val, &size, NULL, &type, NULL, NULL);
        if (status == ERROR_NO_MORE_ITEMS) break;
        if (status == ERROR_SUCCESS) {
            vals.push_back(std::string(val));
            continue;
        }
        printf("failed enumerating %s\n", argv8[2]);
        return 1;
    }

    typedef std::vector<std::string>::iterator vsi;
    for (vsi i = vals.begin(); i != vals.end(); i++) {
        status = RegDeleteValue(key, i->c_str());
        if (status != ERROR_SUCCESS) {
            printf("failed deleting %s\n", i->c_str());
            return 1;
        }
    }

    return 0;
}

Compile this source code via mingw or visual studio and you'll have the regdelete.exe program.

Flash Cookies

Flash cookies reside in C:\Users\<username>\AppData\Roaming\Macromedia\Flash Player\*. The easiest way is to get rid everything in there:

@echo off

set FlashCookies=C:\Users\%USERNAME%\AppData\Roaming\Macromedia\Flashp~1

del /q /s /f "%FlashCookies%"
rd /s /q "%FlashCookies%"

That's it. Have fun clearing that cache!

Hey guys, here are the lastest StackVM news. So we applied to Y Combinator for the Winter 2011 funding cycle but got rejected. On November 2 we got an email saying that they could not accept our application for funding, which meant that we wouldn't even get the interview. Our plan was to surprise Y Combinator folks at the interview by launching a private beta version of our software one day before the interview so that at the time we got interviewed the story was on the Hacker News front page. Well, now we can't surprise them this way, but we can surprise everyone else by saying that we are launching public beta!

Our first product is built on top of StackVM and is called Browserling. It's an interactive cross-browser testing tool inside of your browser. A real browser inside of your browser! Just go to browserling.com to try it out! (please use Chrome.)

In case there is a long queue of people willing to try Browserling, we also made a short video that shows what it is and how it works:

Browserling is a freemium based product. If you register, you get 5 mins for free for as many times as you wish (but you have to wait in the queue again with everyone else). If you don't register, it's just 1min 30secs for as many times as you wish. We haven't figured out the paid plans yet, but we'll get there shortly.

As Browserling is beta, we currently provide Internet Explorer versions 5.5, 6, 7, 8, and 9, (version 5.5, 6, 7, 8 via IETester, and version 9 is native IE), Chrome, Firefox, Safari and Opera.

Browserling is also just the first of many projects that we'll be launching. The next one is going to bring arbitrary desktop applications to the web, and then we have a whole bunch more planned. All these projects will be based on the StackVM technology!

If you're interested in how all of this develops, please subscribe to my blog and follow me on Twitter!

Also, if you use IRC, join #stackvm channel on irc.freenode.net and have fun with us!

I wrote an awesome node.js module for use at StackVM - a module called node-lazy that does lazy list processing through events!

It comes really handy when you need to treat a stream of events like a list. The best use case currently is returning a lazy list from an asynchronous function, and having data pumped into it via events. In asynchronous programming you can't just return a regular list because you don't yet have data for it. The usual solution so far has been to provide a callback that gets called when the data is available. But doing it this way you lose the power of chaining functions and creating pipes, which leads to not that nice interfaces. (See the 2nd example below to see how it improved the interface in one of my modules.)

Check out this toy example:

var Lazy = require('lazy');

var lazy = new Lazy;
lazy
  .filter(function (item) {
    return item % 2 == 0
  })
  .take(5)
  .map(function (item) {
    return item*2;
  })
  .join(function (xs) {
    console.log(xs);
  });

This code says that lazy is going to be a lazy list that filters even numbers, takes first five of them, then multiplies all of them by 2, and then calls the join function (think of join as in threads) on the final list.

And now you can emit data events with data in them at some point later,

[0,1,2,3,4,5,6,7,8,9,10].forEach(function (x) {
  lazy.emit('data', x);
});

The output will be produced by the join function, which will output the expected [0, 4, 8, 12, 16].

And here is a real-world example. Some time ago I wrote a hash database for node.js called node-supermarket (think of key-value store except greater). Now it had a similar interface as a list, you could .forEach on the stored elements, .filter them, etc. But being asynchronous in nature it lead to the following code, littered with callbacks and temporary lists:

var Store = require('supermarket');

var db = new Store({ filename : 'users.db', json : true });

var users_over_20 = [];
db.filter(
  function (user, meta) {
    // predicate function
    return meta.age > 20;
  },
  function (err, user, meta) {
    // function that gets executed when predicate is true
    if (users_over_20.length < 5)
      users_over_20.push(meta);
  },
  function () {
    // done function, called when all records have been filtered

    // now do something with users_over_20
  }
)

This code selects first five users who are over 20 years old and stores them in users_over_20.

But now we changed the node-supermarket interface to return lazy lists, and the code became:

var Store = require('supermarket');

var db = new Store({ filename : 'users.db', json : true });

db.filter(function (user, meta) {
    return meta.age > 20;
  })
  .take(5)
  .join(function (xs) {
    // xs contains the first 5 users who are over 20!
  });

This is so much nicer!

If you wish to try node-lazy just do npm install lazy! Alternatively, if you don't have npm, you can git clone http://github.com/pkrumins/node-lazy.git and set your NODE_PATH environment variable to point to that directory.

Enjoy and follow the future node-lazy developments in its github repo - node-lazy at github!

Article Sponsors

None!

Want to sponsor my future (or past) articles? Contact me for prices and options!

I was interviewed recently on 2011-10-04 by Michael Matuzak from Lambdaphant. I copied the interview here in case Michael's website ever changes or goes down. (Update: Michael's website changed. Here is the original interview from the archive.).

This first interview is with Peteris Krumins who runs the blog catonmat. Peteris is also a founder along with James Halliday of StackVM.

How did you get started in programming?

I don't really know. All I remember is that I have always wanted to be a programmer. From the first day I learned about computer programming, whenever it was, I wanted to be a programmer. I didn't really get stared until I met this person on IRC in around 1996, who knew everything about computers and programming and he helped me a bunch with getting started with Unix and C programming.

I've read that you initially wrote an IRC client as a first project. What language was that in?

That was the first kind-of-a-big-project, not really the very first. I had messed around with various languages and written tens of toy programs before. So at that time I was doing mIRC scripting, messing with Eggdrop bots and decided to create my own IRC client. I first tried to write it in C++ and MFC but it was beyond me at that age but Visual Basic was really straight forward and I wrote a fully working IRC client pretty quickly.

What methods did you use to teach yourself knowing very little about programming?

I'd program by reading tons of source code of other programs. At that time I didn't have Internet access, so I'd carry a pack of floppies around with me and fill them with source code of various programs whenever I had access to Internet somewhere, and then compile and study them at home.

What advice would you give to kids interested in programming now?

My advice is to start programming in a language with light syntax. Like I remember I couldn't really understand the C++ syntax, with all the template and class stuff but mIRC scripting and Visual Basic at the same time was really straight forward. It was also important that I saw the results quickly, so I'd recommend kids to use a language that can create GUI really easily, perhaps I'd even recommend using the same Visual Basic and just creating all kinds of toy programs, like animated games, twitter clients, network chat programs and similar small programs.

Like most hackers you use many different languages. I'm sure you try and use the best tool for the job to get work done (unless you are purposefully using the wrong tool to learn), but let's imagine that you had to pick one language to use for the rest of your life. What would that be?

It would be Haskell because you can't get more functional than that and I love functional programming, it makes the code so elegant and keeps your mind busy trying to come up with the most beautiful abstractions.

Over the last couple of years your blog has become pretty popular. The great content is significant in making that happen. How did you go about gaining readers, and do you think the programming subreddit and HN were significant in gaining popularity?

Yes, the great content is absolutely the key. If you write about something passionately and thoroughly people will notice your blog and start following. So I knew from the first day that social media was the way to gain popularity. I'd submit all my posts to reddit, and later when Hacker News was created, I'd submit them there too, and also ask my friends to Stumble my posts and I'd submit them to delicious, and tell everyone on IRC, and post my articles to Linux and related forums. But even with all this effort after the first year of blogging I only had 1000 RSS subscribers, it took another year to get to 7000 and then another year to 12000.

James Halliday and yourself are working on a start-up called StackVM. What gave you the idea for StackVM? How long until users will see an official beta?

It was actually James's idea. He first created a working prototype in Haskell in January 2010, and then he showed it to me. At that time we both knew each other already and had talked about startups and ycombinator and I really enjoyed James's passion for functional programming. And then in March James offered to do a startup. I had actually wanted to do a startup since 2004 or 2005, when I remember telling my friends about Paul Graham and his startup essays but I hadn't met the right person. This was finally a great opportunity to do what I had dreamed about and so it all started. I am moving to Oakland to work together with James in a few weeks and after a few more weeks we'll have the official beta. We already have quite a powerful server for doing beta!

How are you liking start-up life?

It's not any different than the regular life so I love it.

Are you going to Start-up school this year? If so who are you most excited to see speak?

No, I am not going. It takes place in October 16 and I'll be in Bay Area only in early November.

What is your favorite music to hack away to?
No music actually required.

What is your favorite fiction book?

I don't have a favorite fiction book. I have been focusing on reading academic and scientific books. From these books I'd recommend The New Turing Omnibus, which contains 66 awesome lightweight articles on various fundamental computer science topics.