diff -r -u popa3d-0.4/Makefile popa3d-0.4-apop/Makefile --- popa3d-0.4/Makefile Mon Jan 31 22:56:46 2000 +++ popa3d-0.4-apop/Makefile Thu May 25 20:12:37 2000 @@ -12,6 +12,11 @@ protocol.o database.o mailbox.o \ misc.o +all: popauth popa3d + +popauth: popauth.o + $(LD) $(LDFLAGS) -o $@ popauth.o + popa3d: $(OBJS) $(LD) $(LDFLAGS) $(OBJS) -o popa3d @@ -22,4 +27,4 @@ $(CC) $(CFLAGS) $*.c clean: - $(RM) $(PROJ) $(OBJS) + $(RM) $(PROJ) $(OBJS) *.o popauth *~ diff -r -u popa3d-0.4/params.h popa3d-0.4-apop/params.h --- popa3d-0.4/params.h Tue Feb 1 01:16:24 2000 +++ popa3d-0.4-apop/params.h Sat May 27 03:37:36 2000 @@ -65,6 +65,12 @@ #define AUTH_SHADOW 1 /* + * RFC 1460 APOP authentication support by Dug Song + */ +#define AUTH_APOP 1 +#define POP_AUTH_DB "/etc/pop.auth" + +/* * A salt used to waste some CPU time on dummy crypt(3) calls and make * it harder (but still far from impossible, on most systems) to check * for valid usernames. Adjust it for your crypt(3). diff -r -u popa3d-0.4/pop_auth.c popa3d-0.4-apop/pop_auth.c --- popa3d-0.4/pop_auth.c Wed Mar 24 23:25:55 1999 +++ popa3d-0.4-apop/pop_auth.c Sat May 27 01:36:12 2000 @@ -20,6 +20,20 @@ return POP_LEAVE; } +#if AUTH_APOP +static int pop_auth_apop(char *params) +{ + char *user, *pass; + + user = pop_get_param(¶ms); + if (!user || pop_user) return POP_ERROR; + pass = pop_get_param(¶ms); + if (!pass || pop_pass) return POP_ERROR; + if (!(pop_user = strdup(user)) || !(pop_pass = strdup(pass))) + return POP_CRASH; + return POP_STATE; +} +#else static int pop_auth_user(char *params) { char *user; @@ -36,23 +50,42 @@ if (!(pop_pass = strdup(params))) return POP_CRASH; return POP_STATE; } +#endif /* AUTH_APOP */ static struct pop_command pop_auth_commands[] = { {"QUIT", pop_auth_quit}, +#if AUTH_APOP + {"APOP", pop_auth_apop}, +#else {"USER", pop_auth_user}, {"PASS", pop_auth_pass}, +#endif {NULL} }; int do_pop_auth(int channel) { +#if AUTH_APOP + char challenge[512], hostname[512]; + + if (gethostname(hostname, sizeof(hostname))) return 1; +#endif pop_init(); +#if AUTH_APOP + snprintf(challenge, sizeof(challenge), "<%d.%d@%s>", + getpid(), time(NULL), hostname); + + if (pop_reply("+OK %s", challenge)) return 1; +#else if (pop_reply_ok()) return 1; - +#endif pop_user = NULL; if (pop_handle_state(pop_auth_commands) == POP_STATE) { write_loop(channel, (char *)&pop_buffer, sizeof(pop_buffer)); +#if AUTH_APOP + write_loop(channel, challenge, strlen(challenge) + 1); +#endif write_loop(channel, pop_user, strlen(pop_user) + 1); write_loop(channel, pop_pass, strlen(pop_pass) + 1); if (close(channel)) return 1; diff -r -u popa3d-0.4/pop_root.c popa3d-0.4-apop/pop_root.c --- popa3d-0.4/pop_root.c Tue Feb 1 01:18:12 2000 +++ popa3d-0.4-apop/pop_root.c Sat May 27 03:36:00 2000 @@ -21,13 +21,32 @@ #include "pop_auth.h" #include "pop_trans.h" -#if AUTH_SHADOW +#if AUTH_APOP +#include +#include +#if GDBM +# include +#else +# include +#endif +#elif AUTH_SHADOW #include #ifdef __GLIBC__ #include #endif #endif +/* *DBM compatibility nonsense. */ +#if GDBM +#define DBM GDBM_FILE +#define F_RDONLY GDBM_READER +#define dbm_open(x, y, z) gdbm_open(x, 512, y, z, 0) +#define dbm_fetch gdbm_fetch +#define dbm_close gdbm_close +#else +#define F_RDONLY O_RDONLY +#endif + static struct passwd pop_pw; static char *mailbox; @@ -73,7 +92,51 @@ return offset; } -#if AUTH_SHADOW +#if AUTH_APOP +/* + * RFC 1460 APOP authentication routine. + */ +static struct passwd *do_apop_auth(char *user, char *pass, char *challenge) +{ + struct passwd *pw; + DB *db; + datum key, value; + MD5_CTX ctx; + char hash[33]; + int i; + + pw = getpwnam(user); + endpwent(); + + if (!pw) return (NULL); + memset(pw->pw_passwd, 0, strlen(pw->pw_passwd)); + mailbox = user; + + if ((db = dbm_open(POP_AUTH_DB, F_RDONLY, 0)) == NULL) + return NULL; + + key.dptr = user; + key.dsize = strlen(user) + 1; + value = dbm_fetch(db, key); + + if (value.dptr == NULL) { + dbm_close(db); + return NULL; + } + for (i = 0; i < value.dsize - 1; i++) + value.dptr[i] ^= 0xff; + + MD5Init(&ctx); + MD5Update(&ctx, challenge, strlen(challenge)); + MD5Update(&ctx, value.dptr, strlen(value.dptr)); + MD5End(&ctx, hash); + dbm_close(db); + + if (strcmp(pass, hash)) pw = NULL; + + return (pw); +} +#elif AUTH_SHADOW /* * The /etc/shadow authentication routine. This one is really tricky, * in order to make sure we don't have an /etc/shadow fd or sensitive @@ -162,6 +225,9 @@ static int do_root_auth(int channel) { static char auth[AUTH_BUFFER_SIZE + 2]; +#if AUTH_APOP + char *challenge; +#endif char *user, *pass; struct passwd *pw; @@ -176,11 +242,17 @@ /* Now, the authentication data. */ memset(auth, 0, sizeof(auth)); /* Ensure the NUL termination */ if (read_loop(channel, auth, AUTH_BUFFER_SIZE) < 0) return AUTH_NONE; - +#if AUTH_APOP + challenge = auth; + user = &challenge[strlen(challenge) + 1]; +#else user = auth; +#endif pass = &user[strlen(user) + 1]; -#if AUTH_SHADOW +#if AUTH_APOP + if (!(pw = do_apop_auth(user, pass, challenge))) return AUTH_FAILED; +#elif AUTH_SHADOW if (!(pw = do_shadow_auth(user, pass))) return AUTH_FAILED; #else if (!(pw = do_passwd_auth(user, pass))) return AUTH_FAILED; diff -r -u popa3d-0.4/popauth.8 popa3d-0.4-apop/popauth.8 --- popa3d-0.4/popauth.8 Sat May 27 01:39:31 2000 +++ popa3d-0.4-apop/popauth.8 Sat May 27 01:38:40 2000 @@ -0,0 +1,34 @@ +.TH POPAUTH 8 +.ad +.fi +.SH NAME +popauth +\- +manipulate POP authentication DB +.SH SYNOPSIS +.na +.nf +.fi +\fBpopauth\fR [\fB-d\fR|\fB-l\fR] [\fIuser\fR] +.SH DESCRIPTION +.ad +.fi +\fBpopauth\fR allows a user to change their POP password. In +addition, the superuser may use this program to either add or remove a +user's entry, or to print public information from it. +.LP +Under normal usage, \fBpopauth\fR will prompt to change the user's +password, like passwd(8). +.SH OPTIONS +.IP \fB-l\fR +List the entry for the user, or for all users. +.IP \fB-d\fR +Delete the entry for the user, or for all users. +.SH FILES +\fI/etc/pop.auth.*\fR POP authentication DB +.SH "SEE ALSO" +passwd(8), popa3d(8) +.SH AUTHOR +.na +.nf +Dug Song diff -r -u popa3d-0.4/popauth.c popa3d-0.4-apop/popauth.c --- popa3d-0.4/popauth.c Sat May 27 01:39:31 2000 +++ popa3d-0.4-apop/popauth.c Sat May 27 02:36:19 2000 @@ -0,0 +1,307 @@ +/* + popauth.c + + qpopper-compatible APOP password utility for Solar Designer's popa3d. + + Copyright (c) 2000 Dug Song + All rights reserved, all wrongs reversed. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. The name of author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if GDBM +# include +#else +# include +#endif + +#include "params.h" + +/* *DBM compatibility nonsense. */ +#if GDBM +#define DBM GDBM_FILE +#define F_WRCREAT GDBM_WRCREAT +#define F_RDONLY GDBM_READER +#define F_RDWR GDBM_WRITER +#define F_WRTRUNC GDBM_NEWDB +#define dbm_open(x, y, z) gdbm_open(x, 512, y, z, 0) +#define dbm_firstkey gdbm_firstkey +#define dbm_fetch gdbm_fetch +#define dbm_delete gdbm_delete +#define dbm_close gdbm_close +#else +#define F_WRCREAT O_RDWR|O_CREAT +#define F_RDONLY O_RDONLY +#define F_RDWR O_RDWR +#define F_WRTRUNC O_RDWR|O_TRUNC +#endif + +int Opt_delete = 0; +int Opt_list = 0; + +struct passwd *pw; + +void +usage(void) +{ + fprintf(stderr, "Usage: popauth [-d|-l] [user]\n"); + exit(1); +} + +int +check_privs(char *user) +{ + if ((pw = getpwuid(getuid())) == NULL) { + perror("getpwuid"); + return (0); + } + if (pw->pw_uid != 0) { + if (user == NULL) { + if (Opt_delete || Opt_list) + return (0); + } + else if (strcmp(user, pw->pw_name) != 0) + return (0); + } + return (1); +} + +void +revoke_privs(void) +{ + gid_t groups[2]; + + if (!pw->pw_uid) return; + + groups[0] = groups[1] = pw->pw_gid; + + if (setgroups(1, groups)) { + perror("setgroups"); + exit(1); + } + if (setgid(pw->pw_gid)) { + perror("setgid"); + exit(1); + } + if (setuid(pw->pw_uid)) { + perror("setuid"); + exit(1); + } +} + +int +popauth_modify(char *user) +{ + DB *db; + datum key, value; + char *p, pass[128]; + + if (user == NULL) { + user = pw->pw_name; + } + if ((db = dbm_open(POP_AUTH_DB, F_WRCREAT, S_IRUSR|S_IWUSR)) == NULL) { + perror("dbm_open"); + return (1); + } + revoke_privs(); + + key.dptr = user; + key.dsize = strlen(user) + 1; + + printf("Changing POP password for %s\n", user); + + for (;;) { + p = getpass("New password:"); + if (*p == '\0') break; + + if (strlen(p) < 6) { + printf("Please enter a longer password.\n"); + continue; + } + strncpy(pass, p, sizeof(pass) - 1); + pass[sizeof(pass) - 1] = '\0'; + + p = getpass("Retype new password:"); + if (strcmp(pass, p) == 0) break; + + printf("Passwords don't match, try again.\n"); + } + if (*pass != '\0') { + for (p = pass; *p != '\0'; p++) + *p ^= 0xff; + + value.dptr = pass; + value.dsize = strlen(pass) + 1; + + if (dbm_store(db, key, value, DBM_REPLACE) != 0) { + fprintf(stderr, "dbm_store: database corrupt?\n"); + dbm_close(db); + return (1); + } + } + else printf("Password unchanged.\n"); + + dbm_close(db); + return (0); +} + +int +popauth_list(char *user) +{ + DB *db; + datum key, value; + + if ((db = dbm_open(POP_AUTH_DB, F_RDONLY, 0)) == NULL) { + perror("dbm_open"); + return (1); + } + revoke_privs(); + + if (user != NULL) { + key.dptr = user; + key.dsize = strlen(user) + 1; + + value = dbm_fetch(db, key); + + if (value.dptr == NULL) { + dbm_close(db); + return (1); + } + printf("%-16s: APOP\n", user); + } + else { + for (key = dbm_firstkey(db); key.dptr != NULL; + key = dbm_nextkey(db)) { + value = dbm_fetch(db, key); + + if (value.dptr != NULL) + printf("%-16s: APOP\n", key.dptr); + } + } + dbm_close(db); + return (0); +} + +int +popauth_delete(char *user) +{ + DB *db; + datum key, value; + char buf[32]; + + if (user != NULL) { + if ((db = dbm_open(POP_AUTH_DB, F_RDWR, 0)) == NULL) { + perror("dbm_open"); + return (1); + } + revoke_privs(); + + key.dptr = user; + key.dsize = strlen(user) + 1; + + value = dbm_fetch(db, key); + + if (value.dptr == NULL) { + printf("Unknown user %s.\n", user); + dbm_close(db); + return (1); + } + if (dbm_delete(db, key) < 0) { + fprintf(stderr, "dbm_delete: unable to delete " + "user %s\n", user); + dbm_close(db); + return (1); + } + } + else { + printf("Delete all POP authentication entries? "); + + if (fgets(buf, sizeof(buf), stdin) == NULL || *buf != 'y') + return (1); + + if ((db = dbm_open(POP_AUTH_DB, F_WRTRUNC, 0)) == NULL) { + perror("dbm_open"); + return (1); + } + } + dbm_close(db); + return (0); +} + +int +main(int argc, char *argv[]) +{ + char *user; + int c; + + user = NULL; + + while ((c = getopt(argc, argv, "dlh")) != -1) { + switch (c) { + case 'd': + Opt_delete = 1; + break; + case 'l': + Opt_list = 1; + break; + case '?': + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (argc == 1) + user = argv[0]; + else if (argc > 1) + usage(); + + if (Opt_delete && Opt_list) + usage(); + + if (!check_privs(user)) { + printf("Permission denied.\n"); + return (1); + } + if (Opt_delete) { + return (popauth_delete(user)); + } + else if (Opt_list) { + return (popauth_list(user)); + } + return (popauth_modify(user)); +} + +/* 5000. */