diff options
author | Camiel Dobbelaar <camield@cvs.openbsd.org> | 2001-08-13 20:03:22 +0000 |
---|---|---|
committer | Camiel Dobbelaar <camield@cvs.openbsd.org> | 2001-08-13 20:03:22 +0000 |
commit | 9b80930a98b45e288345a7e869d320fab4f0a698 (patch) | |
tree | 1fd86ad990f07f8f8868ecdca6c0e0d38d845fca /libexec/popa3d | |
parent | ef62c6d658b92504a3b38095b540e66cefb1d83f (diff) |
Solar Designer's popa3d POP3 daemon, version 0.4.9.1
Changes so far:
- removed auth_pam.c
- removed auth_shadow.c
- add BSD makefile
- remove md5, in favour of libc md5
- params.h: AUTH_PASSWD and MAIL_SPOOL_PATH
Diffstat (limited to 'libexec/popa3d')
-rw-r--r-- | libexec/popa3d/DESIGN | 204 | ||||
-rw-r--r-- | libexec/popa3d/LICENSE | 9 | ||||
-rw-r--r-- | libexec/popa3d/Makefile | 10 | ||||
-rw-r--r-- | libexec/popa3d/VIRTUAL | 5 | ||||
-rw-r--r-- | libexec/popa3d/auth_passwd.c | 39 | ||||
-rw-r--r-- | libexec/popa3d/database.c | 86 | ||||
-rw-r--r-- | libexec/popa3d/database.h | 53 | ||||
-rw-r--r-- | libexec/popa3d/virtual.h | 34 |
8 files changed, 440 insertions, 0 deletions
diff --git a/libexec/popa3d/DESIGN b/libexec/popa3d/DESIGN new file mode 100644 index 00000000000..1d03aad76aa --- /dev/null +++ b/libexec/popa3d/DESIGN @@ -0,0 +1,204 @@ +This file describes the design goals and the reasoning behind some of +the design decisions for my tiny POP3 daemon, popa3d. + + + Why popa3d? + +There're lots of different POP3 servers -- with different feature +sets, performance, and reliability. However, as far as I know, before +I started the work on popa3d, there had been only one with security as +one of its primary design goals: qmail-pop3d. Unfortunately, it would +only work with qmail, and only with its new maildir format. While +both qmail and maildirs do indeed have some advantages, a lot of +people continue running other MTAs, and/or use the older mailbox +format, for various reasons. Many of them need a POP3 server. + + + The design goals. + +Well, the goals themselves are obvious; they're probably the same for +most other POP3 servers as well. It's their priority that differs. +For popa3d, the goals are: + +1. Security (to the extent that is possible with POP3 at all, of course). +2. Reliability (again, as limited by the mailbox format and the protocol). +3. RFC compliance (slightly relaxed to work with real-world POP3 clients). +4. Performance (limited by the more important goals, above). + +Obviously, just like the comments indicate, none of the goals can be met +completely, and balanced decisions need to be made. + + + Security. + +First, it is important that none of the popa3d users get a false sense +of security just because it was the primary design goal. The POP3 +protocol transmits passwords in plaintext, and thus, if you care about +the security of your individual user accounts, should only be used +either in trusted networks, or tunneled over encrypted channels. +There exist extensions to the protocol that are supposed to fix this +problem. I am not supporting them yet, partly because this isn't +going to fully fix the problem. In fact, APOP and the weaker defined +SASL mechanisms such as CRAM-MD5 may potentially be even less secure +than transmission of plaintext passwords because of the requirement +that plaintext equivalents are stored on the server. + +It is also important to understand that nothing can be perfectly +secure. I can make mistakes. While the design of popa3d makes it +harder for those to turn into security holes, this is nevertheless +still possible. + +Having that said, let's get to the security-critical design decisions. + + + Privilege management. + +Initially, popa3d is started as root to handle a connection. However, +it does very little work as root: switching to less privileged UIDs, +communication with child processes, and authentication information +checks (which often involve accessing shadow or master.passwd files). + +The following privilege switches happen during a successful POP3 +session, with /etc/shadow authentication: + + startup as root + | + ----------------- + |child |parent + v v + drop to user popa3d, still as root, + handle the AUTHORIZATION wait for and + state, write the results, - - > read the authentication + and exit information + | + ----------------- + |child |parent + v v + getspnam(3), crypt(3), wait for and + check, write the result, - - > read the authentication + and exit (to clean up) result + | + v + drop to the authenticated user, + handle the TRANSACTION state, + possibly UPDATE the mailbox, + and exit + + + Trust. + +No part of popa3d trusts any information obtained from external +sources (that is, the data is never assumed to be of the expected +format, and is treated as subject to authorization checks). This +includes POP3 commands, mailbox contents, and even popa3d's own +less-privileged child process for the AUTHORIZATION state handling. + + + DoS attacks. + +Just like with most other software, there exist ways to cause a Denial +of Service, by supplying popa3d with an enormous amount of otherwise +valid input. I am aware of the following attacks on popa3d itself: + +1. Connection flood. When running in the standalone mode, popa3d does +quite a few checks to significantly reduce the impact of such attacks +by limiting resource consumption (child processes and logging rate), +while still providing full service for other source IP addresses and +logging everything that might be important. However, when running +from an inetd clone, the handling of these attacks is left up to your +inetd and the kernel. + +2. Huge mailbox sizes, either in message count or bytes. There're +limits in popa3d (see params.h) that are intended to prevent this +attack from stopping the entire service. Depending on your disk and +other quotas, it may still be possible to stop individual users from +getting their mail. + + + Reliability. + +Quoting Dan Bernstein, "the mbox format ... is inherently unreliable". + +While popa3d, just like other mail software that deals with mailboxes, +doesn't guarantee reliability over system crashes, it still makes +sense to talk about its operation on an otherwise stable system. + + + Interaction with other MUAs. + +Similarly to cucipop (but unlike qpopper), popa3d works on the +original mailbox file, without copying. However, unlike cucipop, +popa3d is able to ensure that the mailbox doesn't get corrupted if +another MUA modifies it during the POP session. Before each mailbox +access, popa3d checks its timestamp and, if that has changed, +determines if that is due to new mail that has just been delivered, or +other changes made to the mailbox. In the latter case, the POP +session is silently aborted (which doesn't violate the RFC). popa3d +is careful to make sure the timestamp will change if the mailbox is +written to, by keeping the lock for up to a second if necessary. + + + Mailbox access. + +Except for the total size and message count limits mentioned above +(and you can disable even those), there're no other artificial limits +on the mailbox contents. In particular, there're no line length +limits; unlike with qmail-pop3d, lines don't even need to fit in the +available memory. NUL bytes are allowed in messages as well. + + + Locking. + +Because of dropping to the user "completely" (that is, not even +keeping a GID of mail like some other POP3 servers do), popa3d only +uses fcntl(2) or flock(2) for locking. As a result, it may not be +safe over NFS. This is where I choose security over either +functionality or reliability. + + + RFC compliance. + +I tried to make popa3d as strictly RFC 1939 compliant as possible. +Most other POP3 servers have extra "features" that violate the RFC. +Examples include: wrapping long commands (no matter if they're valid +or not) and thus generating multiple -ERR responses (if not even +worse: processing something from the middle of the line as a command) +to a single command, processing "LIST 4294967297" as "LIST 1" instead +of reporting the error, ignoring past a NUL byte till end of line and +thus misinterpreting the command. While these are mostly harmless, +they can theoretically cause a POP3 client not to detect the +unavailability of a protocol extension. + +There's however one place where popa3d's RFC compliance is +deliberately relaxed: popa3d accepts commands terminated by single +LF's, even though the RFC says the commands are terminated by a CRLF +pair. + + + Performance. + +Despite the two extra "security" fork(2) calls, popa3d seems to behave +fairly efficiently: the efficient mailbox parsing code and the lack of +mailbox copying compensate for the extra fork's. + +Here's some real performance data that I've collected (popa3d running +via inetd; larger sites would use the standalone mode instead): + + 24864 295.50re 16.92cp popa3d* + 12749 4578.88re 15.50cp popa3d + +That is, 12749 POP3 sessions took 32.42 minutes of CPU time (on a 350 +MHz Pentium II); of those, more than a half was spent in the temporary +child processes. It's not that bad though, as this system was running +an (intentionally) expensive crypt(3) that got accounted to the child +/etc/shadow authentication processes. + +Before upgrading to popa3d, the same machine was running qpopper (out +of inetd, too): + + 12025 3169.38re 35.56cp popper + +It used to take a bit more CPU for less POP3 sessions. + +-- +Solar Designer <solar@openwall.com> diff --git a/libexec/popa3d/LICENSE b/libexec/popa3d/LICENSE new file mode 100644 index 00000000000..dc50971f05d --- /dev/null +++ b/libexec/popa3d/LICENSE @@ -0,0 +1,9 @@ +You're allowed to do whatever you like with this software (including +re-distribution in source and/or binary form, with or without +modification), provided that credit is given where it is due and any +modified versions are marked as such. There's absolutely no warranty. + +Note that you don't have to re-distribute this software under these +same relaxed terms. In particular, you're free to place modified +versions under (L)GPL, thus disallowing further re-distribution in +binary-only form. diff --git a/libexec/popa3d/Makefile b/libexec/popa3d/Makefile new file mode 100644 index 00000000000..7f9f429fa8c --- /dev/null +++ b/libexec/popa3d/Makefile @@ -0,0 +1,10 @@ +# $OpenBSD: Makefile,v 1.1 2001/08/13 20:03:21 camield Exp $ + +PROG= popa3d + +SRCS= auth_passwd.c database.c mailbox.c misc.c pop_auth.c pop_root.c \ + pop_trans.c protocol.c standalone.c virtual.c + +NOMAN= + +.include <bsd.prog.mk> diff --git a/libexec/popa3d/VIRTUAL b/libexec/popa3d/VIRTUAL new file mode 100644 index 00000000000..23341277b81 --- /dev/null +++ b/libexec/popa3d/VIRTUAL @@ -0,0 +1,5 @@ +The virtual domain support in popa3d is in development and is currently +undocumented. + +Please only use it if you know what you are doing, -- it is too easy to +misconfigure it in dangerous ways. diff --git a/libexec/popa3d/auth_passwd.c b/libexec/popa3d/auth_passwd.c new file mode 100644 index 00000000000..40642a2d8b2 --- /dev/null +++ b/libexec/popa3d/auth_passwd.c @@ -0,0 +1,39 @@ +/* + * The /etc/passwd authentication routine. + */ + +#include "params.h" + +#if AUTH_PASSWD && !VIRTUAL_ONLY + +#define _XOPEN_SOURCE +#define _XOPEN_SOURCE_EXTENDED +#define _XOPEN_VERSION 4 +#define _XPG4_2 +#include <unistd.h> +#include <string.h> +#include <pwd.h> +#include <sys/types.h> + +struct passwd *auth_userpass(char *user, char *pass, char **mailbox) +{ + struct passwd *pw, *result; + + if ((pw = getpwnam(user))) *mailbox = user; + endpwent(); + result = NULL; + + if (!pw || !*pw->pw_passwd || + *pw->pw_passwd == '*' || *pw->pw_passwd == '!') + crypt(pass, AUTH_DUMMY_SALT); + else + if (!strcmp(crypt(pass, pw->pw_passwd), pw->pw_passwd)) + result = pw; + + if (pw) + memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); + + return result; +} + +#endif diff --git a/libexec/popa3d/database.c b/libexec/popa3d/database.c new file mode 100644 index 00000000000..1d6d5b76c42 --- /dev/null +++ b/libexec/popa3d/database.c @@ -0,0 +1,86 @@ +/* + * Message database management. + */ + +#include <stdlib.h> +#include <string.h> + +#include "params.h" +#include "database.h" + +struct db_main db; + +void db_init(void) +{ + db.head = db.tail = NULL; + db.total_count = 0; + db.total_size = 0; + db.flags = 0; +#if POP_SUPPORT_LAST + db.last = 0; +#endif +} + +int db_add(struct db_message *msg) +{ + struct db_message *entry; + + if (db.total_count >= MAX_MAILBOX_MESSAGES) return 1; + + entry = malloc(sizeof(struct db_message)); + if (!entry) return 1; + + memcpy(entry, msg, sizeof(struct db_message)); + entry->next = NULL; + entry->flags = 0; + + if (db.tail) + db.tail = db.tail->next = entry; + else + db.tail = db.head = entry; + + if (++db.total_count <= 0) return 1; + if ((db.total_size += entry->size) < 0 || entry->size < 0) return 1; + + return 0; +} + +int db_delete(struct db_message *msg) +{ + if (msg->flags & MSG_DELETED) return 1; + + msg->flags |= MSG_DELETED; + + db.visible_count--; + db.visible_size -= msg->size; + db.flags |= DB_DIRTY; + + return 0; +} + +int db_fix(void) +{ + int size; + struct db_message *entry; + int index; + + db.visible_count = db.total_count; + db.visible_size = db.total_size; + + if (!db.total_count) return 0; + + size = sizeof(struct db_message *) * db.total_count; + if (size <= 0) return 1; + if (size / sizeof(struct db_message *) != db.total_count) return 1; + + db.array = malloc(size); + if (!db.array) return 1; + + entry = db.head; + index = 0; + do { + db.array[index++] = entry; + } while ((entry = entry->next)); + + return 0; +} diff --git a/libexec/popa3d/database.h b/libexec/popa3d/database.h new file mode 100644 index 00000000000..e35c382d0a6 --- /dev/null +++ b/libexec/popa3d/database.h @@ -0,0 +1,53 @@ +/* + * Message database management. + */ + +#ifndef _POP_DATABASE_H +#define _POP_DATABASE_H + +#include <md5.h> + +#include "params.h" + +/* + * Message flags. + */ +/* Marked for deletion */ +#define MSG_DELETED 0x00000001 + +/* + * Database flags. + */ +/* Some messages are marked for deletion, mailbox update is needed */ +#define DB_DIRTY 0x00000001 +/* Another MUA has modified our part of the mailbox */ +#define DB_STALE 0x00000002 + +struct db_message { + struct db_message *next; + long size; /* Size as reported via POP */ + int flags; /* MSG_* flags defined above */ + long raw_offset, raw_size; /* Raw, with the "From " line */ + long data_offset, data_size; /* Just the message itself */ + unsigned char hash[16]; /* MD5 hash, to be used for UIDL */ +}; + +struct db_main { + struct db_message *head, *tail; /* Messages in a linked list */ + struct db_message **array; /* Direct access to messages */ + int total_count, visible_count; /* Total and not DELEted counts */ + long total_size, visible_size; /* To be reported via POP */ + int flags; /* DB_* flags defined above */ +#if POP_SUPPORT_LAST + int last; /* Last message touched */ +#endif +}; + +extern struct db_main db; + +extern void db_init(void); +extern int db_add(struct db_message *msg); +extern int db_delete(struct db_message *msg); +extern int db_fix(void); + +#endif diff --git a/libexec/popa3d/virtual.h b/libexec/popa3d/virtual.h new file mode 100644 index 00000000000..92173a32bc9 --- /dev/null +++ b/libexec/popa3d/virtual.h @@ -0,0 +1,34 @@ +/* + * Virtual domain support. + */ + +#ifndef _POP_VIRTUAL_H +#define _POP_VIRTUAL_H + +#include <pwd.h> +#include <sys/types.h> + +/* + * These are set by the authentication routine, below. + */ +extern char *virtual_domain; +extern char *virtual_spool; + +/* + * Initializes the virtual domain support at startup. Note that this will + * only be called once in standalone mode, so don't expect an open socket + * here. Returns a non-zero value on error. + */ +extern int virtual_startup(void); + +/* + * Tries to authenticate a username/password pair for the virtual domain + * indicated either by the connected IP address (the socket is available + * on fd 0), or as a part of the username. If the virtual domain is known, + * virtual_domain and virtual_spool are set appropriately. If the username + * is known as well, mailbox is set to the username. Returns the template + * user to run as if the authentication is successful, or NULL otherwise. + */ +extern struct passwd *virtual_userpass(char *user, char *pass, char **mailbox); + +#endif |