diff -rc akpop3d-0.7.4/akpop3d.8 akpop3d-0.7.4-shh/akpop3d.8 *** akpop3d-0.7.4/akpop3d.8 Fri Apr 4 17:42:59 2003 --- akpop3d-0.7.4-shh/akpop3d.8 Fri May 9 22:31:27 2003 *************** *** 110,115 **** --- 110,129 ---- option, .Ar authfile must be specified as a full path. + .Pp + The password may be given either as an MD5 hash or in the clear (not + recommended). If MD5 us used, the value should be "MD5-" followed by + 32 hexadecimal digits (lower case) representing the MD5 output of the + following string: The password, a line feed (ASCII 10), the user name, + a line feed, and the magic string "akpop3d". No trailing line feed + after the magic string. As an example, the password "foo" for user + "bar" would be "MD5-a199706d2b8302a086a20fe9fb4e8403". + .Pp + If the programs printf and md5sum are available, which they typically + are on GNU-based systems, the hash may be calculated using the + following command: + .Pp + printf "foo\\nbar\\nakpop3d" | md5sum .It Fl m Ar spooldir specifies an alternative mail spool directory. The default is .Pa /var/mail/ . diff -rc akpop3d-0.7.4/authenticate.c akpop3d-0.7.4-shh/authenticate.c *** akpop3d-0.7.4/authenticate.c Mon Apr 7 20:05:06 2003 --- akpop3d-0.7.4-shh/authenticate.c Fri May 9 22:31:38 2003 *************** *** 2,7 **** --- 2,8 ---- # include "config.h" #endif + #include "md5.h" #include #include #include *************** *** 15,20 **** --- 16,22 ---- #include #include #include + #include #ifdef HAVE_CRYPT_H #include *************** *** 129,138 **** return 0; } int authenticate(char * username, char * password) { char user[MAXLINE+1], pass[MAXLINE+1]; - char linebuf[MAXLINE+1]; - char * ptr; char * sys_pw; char * crp; struct passwd * u; --- 131,283 ---- return 0; } + /* returns a static buffer containing the string representation of an + * MD5'ified password hash. the string starts with "MD5-". the rest + * of the string is the lower case ASCII representation of an MD5 hash + * (32 bytes). the hash is calculated from the password followed by a + * line feed (ASCII 10), followed by the user name, another line feed, + * and the "magic" string "akpop3d". */ + static char * calc_password_hash(char * username, char * password) { + /* Sverre H. Huseby, 2003-05-09 */ + /* this program is not multi threaded, and we know we won't mix two + * calls to the function, so let's make it simple using a static + * return variable. */ + static char ret[4 + 32 + 1]; + static char * magic = "akpop3d"; + static char * hex_digits = "0123456789abcdef"; + char buf[1024]; + unsigned char md5[16]; + int q; + char * p; + unsigned char * up; + + if (strlen(username) + strlen(password) + strlen(magic) + 3 > sizeof(buf)) { + syslog(LOG_ERR, "overly long username or password"); + return NULL; + } + strcpy(buf, password); + strcat(buf, "\n"); + strcat(buf, username); + strcat(buf, "\n"); + strcat(buf, magic); + + md5_buffer(buf, strlen(buf), md5); + + strcpy(ret, "MD5-"); + p = ret + 4; + up = md5; + for (q = 0; q < 16; q++) { + *p++ = hex_digits[*up >> 4]; + *p++ = hex_digits[*up & 0xf]; + ++up; + } + *p = '\0'; + + return ret; + } + + static int is_password_match(char * username, char * stored_password, + char * given_password) { + /* Sverre H. Huseby, 2003-05-09 */ + char * hash; + char * p1; + char * p2; + int q; + + if (strlen(stored_password) == 4 + 32 + && strncmp(stored_password, "MD5-", 4) == 0) { + /* the password is stored as and MD5 hash */ + hash = calc_password_hash(username, given_password); + p1 = hash + 4; + p2 = stored_password + 4; + /* compare MD5 hashes represented as ASCII (32 bytes) , including + * the terminating NUL byte (an additional byte). could have used + * strcasecmp, but as this function is not supported by all + * systems, we do it the hard way. i guess i should have learned + * to make those fancy autoconf rules, but i haven't. */ + for (q = 0; q < 32 + 1; q++) { + /* tolower may be a macro I guess, so we can't increase the + * p1/p2 pointers in the following if statement. */ + if (tolower(*p1) != tolower(*p2)) + return 0; + ++p1; + ++p2; + } + /* the hashes are equal. */ + return 1; + } else { + /* the password is stored in the clear. yikes! */ + return (strcmp(given_password, stored_password) == 0); + } + } + + /* returns 0 if authentication failed, !0 if it succeeded. */ + static int authenticate_by_file(char * username, char * password) { + char * ptr; + char linebuf[MAXLINE+1]; + FILE * fptr; + + if (authfile == NULL) + return 0; + + fptr = fopen(authfile, "r"); + if (fptr == NULL) { + syslog(LOG_ERR, "%s: %s: %s", + "failed to read auth file", authfile, strerror(errno)); + return 0; + } + while (!ferror(fptr) && !feof(fptr)) { + linebuf[0] = '\0'; + if (fgets(linebuf, sizeof(linebuf), fptr) == NULL) { + fclose(fptr); + return 0; + } + ptr = strtok(linebuf, ":"); + if (ptr == NULL) + continue; + if (strcmp(username, ptr) != 0) + continue; + ptr = strtok(NULL, ":"); + if (ptr == NULL) { + fclose(fptr); + return 0; + } + if (!is_password_match(username, ptr, password)) { + fclose(fptr); + return 0; + } + + /* + * At this point we've authenticated, but we now need to find out + * what Unix username to use and what maildrop file to read + */ + + ptr = strtok(NULL, ":"); + if (ptr == NULL) { + fclose(fptr); + return 0; + } + + strlcpy(real_username, ptr, sizeof(real_username)); + + ptr = strtok(NULL, ":\n"); + if (ptr == NULL) { + real_username[0] = '\0'; + fclose(fptr); + return 0; + } + + fclose(fptr); + strlcpy(real_maildrop, ptr, sizeof(real_maildrop)); + + return 1; + } + fclose(fptr); + return 0; + } + int authenticate(char * username, char * password) { char user[MAXLINE+1], pass[MAXLINE+1]; char * sys_pw; char * crp; struct passwd * u; *************** *** 140,146 **** struct spwd * s; #endif int len; - FILE * fptr; /* sanity checks */ if (username==NULL || password==NULL) { --- 285,290 ---- *************** *** 189,247 **** /* * Text-file authentication */ ! if (authfile != NULL) { ! fptr = fopen(authfile, "r"); ! if (fptr == NULL) { ! syslog(LOG_ERR,"%s: %s: %s","failed to read auth file",authfile,strerror(errno)); ! return 0; ! } ! while ((0!=ferror(fptr)) && (0!=feof(fptr))) { ! linebuf[0] = '\0'; ! if (fgets(linebuf, sizeof(linebuf), fptr) == NULL) { ! fclose(fptr); ! return 0; ! } ! ptr = strtok(linebuf, ":"); ! if (ptr == NULL) continue; ! if (strcmp(user, ptr) != 0) continue; ! ptr = strtok(NULL, ":"); ! if (ptr == NULL) { ! fclose(fptr); ! return 0; ! } ! if (strcmp(pass, ptr) != 0) { ! fclose(fptr); ! return 0; ! } ! ! /* ! * At this point we've authenticated, but we now need to find out what ! * Unix username to use and what maildrop file to read ! */ ! ! ptr = strtok(NULL, ":"); ! if (ptr == NULL) { ! fclose(fptr); ! return 0; ! } ! ! strlcpy(real_username, ptr, sizeof(real_username)); ! ! ptr = strtok(NULL, ":\n"); ! if (ptr == NULL) { ! real_username[0] = '\0'; ! fclose(fptr); ! return 0; ! } ! ! fclose(fptr); ! strlcpy(real_maildrop, ptr, sizeof(real_maildrop)); ! ! return 1; ! } ! fclose(fptr); ! return 0; ! } u = getpwnam(user); --- 333,340 ---- /* * Text-file authentication */ ! if (authfile != NULL) ! return authenticate_by_file(user, pass); u = getpwnam(user);