Fun with Mozilla, emacs, PAM, WU-FTPD, and MT

2002 September 18
by darkness

So today I decided I should set up a web log to keep track of my exploits. Who else would the uninitiated turn to except Blogger ?

I go to Blogger’s site, create an account, and then set about creating an FTP-only account on my system. By “FTP-only” I mean an account that is unable to do anything (including log in to SSH) except FTP in. Anonymous users won’t work for this since I need the account to be password’ed (and separate from the ” ftp ” user, anyway). A regular account won’t do because I don’t want to give anyone that manages to crack Blogger a login and password to my server. One would think this is something that people do all the time, a fairly trivial task. I mean to foreshadow, of course, that one would be wrong in this assumption.

First step to creating such an account with WU-FTPD (default on Red Hat systems, and mine is RH 7.1) is creating a regular system account, but with a valid-but-useless shell and with a specially formatted string for their home directory. I used /sbin/nologin for the shell, which comes in RH’s util-linux package; I had to manually list the shell in /etc/shells to satisfy the “valid” part of “valid-but-useless”. The home directory needs to look like /PATH/TO/CHDIR/./PATH/TO/HOME . Note the /. in the middle, which tells WU-FTPD that when this user logs in it should first chroot to /PATH/TO/CHDIR then chdir to /PATH/TO/HOME .

Next you have to signal the user you just created as a “guest user” with the guestuser <username> directive in /etc/ftpaccess . This combined with the fun home directory above will cause WU-FTPD to chroot this user when they log in.

Most people pretty much stop here. They assign the account a password, try logging in ( /sbin/nologin sends back a pretty message and then promptly exits) and leave it at that. My fear is that another service, such as POP3, might still authenticate the user if I ever decide to use it in the future and forget about my FTP user I just created. Worse, they might be able to have SSH forward ports and connect to them before the login shell could terminate. Not acceptable.

WU-FTPD seems to have little solution for this problem. It has some support for virtual “domains” (really IP addresses, I believe), but using this points it to a whole separate password file as near as I can tell. I want to authenticate both system accounts and FTP-only accounts, so this won’t work. (I wouldn’t be surprised to find that I missed something in WU-FTPD’s configuration, so someone please feel free to enlighten me.)

Instead, I ended up at PAM . Miraculously, this allowed me to do exactly what I want without any real compiling of programs. (I’m kind of lying here since, in the process of debugging things, I may have compiled something. Someone that doesn’t make stupid mistakes, though, can follow what I did without touching gcc’n'friends.) I took my /etc/pam.d/ftp file, which looked like this:

#%PAM-1.0
auth       required	/lib/security/pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth       required	/lib/security/pam_stack.so service=system-auth
auth       required	/lib/security/pam_shells.so
account    required	/lib/security/pam_stack.so service=system-auth
session    required	/lib/security/pam_stack.so service=system-auth

and changed it to this:

#%PAM-1.0
auth       required /lib/security/pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth       required /lib/security/pam_shells.so

#auth       required    /lib/security/pam_stack.so service=system-auth
# From system-auth, slightly modified.
auth        required      /lib/security/pam_env.so
auth        sufficient    /lib/security/pam_userdb.so db=/etc/ftppasswd
auth        sufficient    /lib/security/pam_unix.so likeauth nullok
auth        required      /lib/security/pam_deny.so

account    required /lib/security/pam_stack.so service=system-auth
session    required /lib/security/pam_stack.so service=system-auth

This basically tells PAM to first try authenticating from a Berkeley DB file /etc/ftppasswd.db first, then try authenticating using normal system mechanisms. If either of these succeed, the user is authenticated. Then to create the DB file, something like:

[darkness@darkness darkness]$ db_load -t hash -T ftppasswd.db
user
password
^D
[darkness@darkness darkness]$ 

You can dump the database like:

[darkness@darkness darkness]$ db_dump -p ftppasswd.db
VERSION=2
format=print
type=hash
h_nelem=1
HEADER=END
 user
 password
DATA=END
[darkness@darkness darkness]$ 

Incidentally, you can take the output from db_dump and feed it right back in to db_load ; just leave off the -t hash -T part. There is also a create.pl file that comes with Linux-PAM that will do something similar to the above, but I couldn’t find it in the RH packages so I didn’t really bother with it.

This should be pretty much it. Make sure your FTP user has a disabled password in /etc/shadow (second field for the user’s line should have something like !! in it). Now when they connect via FTP they’ll be authenticated from our separate password database, but if they connect via any other service they’ll be unable to authenticate. Some gotchas:

  • Don’t be an idiot like I was and spend hours figuring out why all you see in the logs is:

    Sep 17 19:12:28 darkness pam_userdb[6802]: user 'my-ftp-user' granted acces
    Sep 17 19:12:28 darkness ftp(pam_unix)[6802]: authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=localhost  user=my-ftp-user
    

    If you get to pam_unix after pam_userdb reports that it “granted acces sic” that means that one of the required modules above it failed. In my case, pam_shells.so was failing because I forgot to put /sbin/nologin in /etc/shells. (Note: RH 7.1 didn’t have it in there, but another system with RH 7.3 already did, so it may be default now.)

  • You can put debug on the end of just about any PAM module and it might spit out debugging information.
  • Note that while I called my database file ftppasswd.db , you leave the .db off the database name when specifying it to pam_userdb.


So now I was reasonably happy. I had Blogger. I had an FTP-only user that Blogger was going to upload to. Feeling good about myself I speed Mozilla to Blogger’s site, log in, click something on their site and POOF no more Mozilla. What’s this? Mozilla has been solid for quite some time now! After repeating this a couple of times, I opt to check my version. GASP! 0.9.9! Oh horror of horrors, 1.1 is out and 1.2a as well, yet I’m wasting away in the lowly depths of a pre-release. Then I remember why: RH 7.3 didn’t ship with Mozilla 1.0 (presumably for QA, compatibility, and/or release deadlines). Luckily, Mozilla has RPMs built just for me and RH 7.x.

A bit later I’ve upgraded to Mozilla 1.1 and I’m once again fairly happy. I then become distressed at the size of the text entry field on the Blogger UI; I run at 1024×768, and this thing wasn’t too nice. Then I remember the slight pain of entering in text in a Mozilla text input widget. Pain enough to make me seek out MozBlog . Their site was down yesterday, but apparently came up today.

I get (don’t forget to install JsLib first). I install (don’t forget to run Mozilla as root to install stuff). It… does weird shit . OK, I read the bug report, find a couple newer versions, install them. After blowing away ~/.mozilla a time or two, I’m getting messages like “error! Null” when I start Mozilla up. Luckily I found somewhere that you need to seek out discussProfile.rdf and blogHistory.rdf in ~/.mozilla and chmod them 600 or so. Now no more errors on startup. I edit my preferences, set up my Blogger web log; it pings, I get happy. Then I realize I have no idea how to pull up this magical MozBlog log entry dialog.

Alt-` I see in some places? Nope. Finally I realize I need to bring up the “Bottombar”, which strikes me as some sort of obscene reference. Now I am graced with the MozBlog interface, which does everything it’s supposed to just fine. Of course, I’m still not satisfied.

It only has a limited number of formatting styles (bold and italics, IIRC; no underline, for example). What appear to be its attempt at “styles” don’t seem to reset from one to the next correctly (though I may have been using it wrong). You can’t insert markup in normal mode. You switch to what looks like “source mode”, as in editing HTML source, and it… tries to stick my log entry on one big line? Not having it. Plus, this text widget feel suspiciously like the normal text widget, with a bigger font size. Uh uh, not having it.

So the Bottombar goes back to the … bottom, and I start thinking about an interface I’d like. Then I realize the answer: Emacs. I’ve been way in to Emacs lately, writing Elisp and everything. I find blogger.el . It requires xml-rpc.el . This, in turn, requires something called “URL” for Emacs. Go to the GNU site for “URL loader” , which is apparently destined to be part of the new Emacs/W3 , and find their CVS pserver to be down. wget’ing their WebCVS interface isn’t happening either. So instead I find what I guess is the old Emacs/W3 page and grab the entire Emacs/W3 from them. Compile, shove in load-path, and I’m now making web log entries from Emacs. Works fabulously. Emacs never ceases to amaze me these days.

By now you’d think I’d be content. Not so, though, as someone starts telling me about Movable Type , which is actually a set of Perl CGI’s that comprise an entire web log management system. (Probably has features beyond this too; I didn’t get much chance to look at it.) Follow the install instructions and you’ll mostly be OK. Don’t forget to run mt-load.cgi (I wonder how many MT web logs still have this file lying about?) before going to mt.cgi. I set mine up in mod_perl; if you do this, be aware of mod_perl instructions for Movable Type . Follow these after you run mt-check.cgi and mt-load.cgi, but before hitting /mt for the first time. My directory structure looks like:

site-directory/
  document-root/  # This is DocumentRoot in Apache configuration.
    blogs/        # This is where I keep my users' blogs.
      users...
    mt-static/    # This contains docs and images from the
      docs/       # MT distribution.
      images/
  perl-bin/       # I need this here to house mt-xmlrpc.cgi.
  data/
    mt/
      db/         # Make sure this is writable by Apache's user.
      tmpl/       # tmpl and import from the MT distribution.
      import/
  lib/
    mt/
      lib/        # This is lib from the MT distribution.

I get errors from time to time, at least during initial setup. Creating a blog would fail, but the blog would be there when I go back to the menu. I could usually delete it, recreate it, and it would bring me to the prefs screen. Don’t forget to hit “Rebuild Site” after you first create a web log or there won’t be any files in the user’s blog directory. Also note that you seem to have to have a blog created to start editing “authors” (or “users” as I like to think of them). So I created an admin user, created a “System Blog”, then created my normal user and its blog. I can probably delete the “System Blog” I guess.

Now I was pretty much happy. Except… titles. MT uses them, blogger.el doesn’t apparently. I think this is because all this stuff was based on the original blogger API which didn’t (doesn’t?) support them. So I made a tiny patch to mt-xmlrpc.cgi from MT 2.21:

--- MT-2.21/mt-xmlrpc.cgi       Mon Jun 24 15:10:38 2002
+++ /srv/www/www.codefu.org/perl-bin/mt-xmlrpc.cgi      Wed Sep 18 03:27:45 2002@@ -97,6 +97,9 @@
     my($appkey, $blog_id, $user, $pass, $item, $publish);
     if ($class eq 'blogger') {
         ($appkey, $blog_id, $user, $pass, my($content), $publish) = @_;
+        if ($content =~ s/^title=([^\n]+)\n+//s) {
+            chomp ($item->{title} = $1);
+        }
         $item->{description} = $content;
     } else {
         ($blog_id, $user, $pass, $item, $publish) = @_;
@@ -147,6 +150,9 @@
     my($appkey, $entry_id, $user, $pass, $item, $publish);
     if ($class eq 'blogger') {
         ($appkey, $entry_id, $user, $pass, my($content), $publish) = @_;
+        if ($content =~ s/^title=([^\n]+)\n+//s) {
+            chomp ($item->{title} = $1);
+        }
         $item->{description} = $content;
     } else {
         ($entry_id, $user, $pass, $item, $publish) = @_;

This allows you to specify “title=” as the first line in a log entry on a line by itself. Any blank lines following this line are deleted. This patch really isn’t complete yet and probably isn’t very nice, but it works for me. I don’t think it’ll put in this title= when retrieving a post, which it probably should — and which I’ll probably do later.

So now I’m happy, and as you may have noticed, I’m blogging from Emacs. And I have a completely useless FTP-only account.

Man, “blogging” sounds stupid. I’m sorry.

(And then as I upload this and see how shitty it looks, I have to turn off auto-convert line breaks, insert my own paragraph breaks, turn off auto-linking… Oh the joy.)

No Comments

Leave A Comment

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS