/* Copyright 1998, Merit Network, Inc. and the University of Michigan */ /* $Id: mailchk.c,v 1.7 2002/10/23 02:58:50 ljb Exp $ * originally Id: mailchk.c,v 1.2 1998/08/10 19:24:23 dogcow Exp */ #include #include #include #include #include #include #include #include #include #include #include /* Includes the PGP cookies, pgpmailauth and pgpkeyauth */ #define BIGSTRBUFLEN 2048 /* space for large strings */ #define SMALLSTRBUFLEN 256 /* space for small strings */ extern trace_t *default_trace; /* Given a transaction file that has been stripped of PGP encoding * information (if there are any PGP signatures), append email header * information and 'password:' fields to each object in the transaction file. * Email header information is the 'From', 'msg-id', 'date', and 'subject' * email fields. * * Recall the ripe181 password convention. If a password field appears * anywhere in a transaction it applies to the rest of the objects. * So this routine looks for any password fields and appends the password * to all subsequent objects in the transaction. * * When we refer to "append" information, we mean append a field of the form: * HDR-XXX: * the syntax checker is designed to recognized these fields as special * and to pass them to the auth checker to use. See hdr_fields.c for a * listing of all the header fields. * * Return: * 0..n (ie, number of non-null lines read) * this routine can detect null submissions * -1 an error occured (eg, opening the input file) */ int addmailcookies (trace_t *tr, int daemon_mode, char *infn, char *outfn) { char curline[BIGSTRBUFLEN], passwd[SMALLSTRBUFLEN]; char email_info[BIGSTRBUFLEN], from_line[SMALLSTRBUFLEN]; char *hdr, *strptr; FILE *infile, *outfile; regex_t mailfromre, mailfromncre, messidre, subjre, datere; regex_t blanklinere, cookieinsre, passwdre, mailreplytore; regmatch_t mailfromrm[4]; int in_headers = 1, count_lines = 0; int first_line = 1, seen_reply_to = 0; int unknown_user = 0; int whichoffset = 0, eo, so, prev, obj_len; int end_obj = 0, src_seen = 0, in_obj = 0, done = 0; infile = myfopen(tr, infn, "r", "addmailcookies(): input file"); if (infile == NULL) return(-1); outfile = myfopen(tr, outfn, "w", "addmailcookies(): output file"); if (outfile == NULL) { fclose(infile); return(-1); } /* initialization */ passwd[0] = '\0'; from_line[0] = '\0'; email_info[0] = '\0'; regcomp(&mailfromre, mailfrom, (REG_EXTENDED|REG_ICASE)); regcomp(&mailreplytore, mailreplyto, (REG_EXTENDED|REG_ICASE)); regcomp(&mailfromncre, mailfromnc, REG_ICASE); regcomp(&messidre, messid, (REG_EXTENDED|REG_ICASE)); regcomp(&subjre, subj, REG_EXTENDED|REG_ICASE); regcomp(&datere, date, REG_EXTENDED|REG_ICASE); regcomp(&blanklinere, blankline, REG_EXTENDED|REG_NOSUB); regcomp(&cookieinsre, cookieins, (REG_NOSUB|REG_EXTENDED|REG_ICASE)); regcomp(&passwdre, password, (REG_EXTENDED|REG_ICASE)); while (!done) { /* loop reading lines from message - allow room to append \n\0 */ done = (NULL == fgets (curline, BIGSTRBUFLEN - 2, infile)) ? 1 : 0; if (done) { if (memcmp (curline, "password:", 9)) { if (curline[strlen (curline) - 1] != '\n') fputs ("\n", outfile); if (curline[0] == '\n') break; } memcpy (curline, "\n", 2); } if (first_line && 0 != regexec (&mailfromncre, curline, 0, 0, 0)) { in_headers = 0; if (!daemon_mode) /* it's not a mail or tcp submission */ unknown_user = 1; } /* The first line _has_ to match /^From/, or else it's not really a mail message. The syntax checker doesn't like the mail headers as an object. :) */ first_line = 0; if (in_headers && regexec (&blanklinere, curline, 0, 0, 0) == 0) { in_headers = 0; if (seen_reply_to == 0 && from_line[0] != '\0') if (strlen(email_info) + strlen(from_line) > BIGSTRBUFLEN) { trace(ERROR, tr, "Email headers exceeds maximum length of %d:\n %s%s", BIGSTRBUFLEN, email_info, from_line); fclose(outfile); unlink(outfn); return(-1); } strcat (email_info, from_line); } /* have to special case /From(:?)/ because it can have stuff in either match position one or three. */ /* pick off the 'From', 'msg-id', 'date', 'subject' from the * the email header (if there is one) */ if (in_headers) { so = eo = -1; hdr = NULL; /* make the compiler happy */ if (0 == regexec(&mailfromre, curline, 4, mailfromrm, 0)) { if (mailfromrm[3].rm_so != -1)/* stuff */ whichoffset = 3; else whichoffset = 1; obj_len = mailfromrm[whichoffset].rm_eo - mailfromrm[whichoffset].rm_so; if (obj_len >= SMALLSTRBUFLEN - strlen(FROM) - 2) { trace(ERROR, tr, "Mail From \"%s\" exceeds maximum length of %d.\n", curline, SMALLSTRBUFLEN - strlen(FROM) - 2); fclose(outfile); unlink(outfn); return(-1); } strcpy (from_line, (char *) FROM); strptr = from_line + strlen(FROM); memcpy (strptr, (char *) (curline + mailfromrm[whichoffset].rm_so), (size_t) (obj_len)); strptr += obj_len; *strptr++ = '\n'; *strptr = '\0'; } /* header snarf */ if (0 == regexec(&mailreplytore, curline, 4, mailfromrm, 0)) { seen_reply_to = 1; if (mailfromrm[3].rm_so != -1)/* stuff */ whichoffset = 3; else whichoffset = 1; so = mailfromrm[whichoffset].rm_so; eo = mailfromrm[whichoffset].rm_eo; hdr = (char *) FROM; } /* header snarf */ else if (0 == regexec(&messidre, curline, 2, mailfromrm, 0)) { so = mailfromrm[1].rm_so; eo = mailfromrm[1].rm_eo; hdr = (char *) MSG_ID; } else if (0 == regexec(&subjre, curline, 2, mailfromrm, 0)) { so = mailfromrm[1].rm_so; eo = mailfromrm[1].rm_eo; hdr = (char *) SUBJECT; } else if (0 == regexec(&datere, curline, 2, mailfromrm, 0)) { so = mailfromrm[1].rm_so; eo = mailfromrm[1].rm_eo; hdr = (char *) DATE; } if (so != -1) { *(curline + eo + 1) = '\0'; *(curline + eo) = '\n'; if (strlen(email_info) + strlen(hdr) + strlen((char *) (curline + so)) > BIGSTRBUFLEN) { trace(ERROR, tr, "Email headers exceeds maximum length of %d:\n %s%s%s", BIGSTRBUFLEN, email_info, hdr, (char *) (curline + so)); fclose(outfile); unlink(outfn); return(-1); } strcat (email_info, hdr); strcat (email_info, (char *) (curline + so)); } } /* else not in headers */ else { prev = in_obj; if (regexec (&blanklinere, curline, (size_t) 0, NULL, 0)) { if (in_obj == 0) { src_seen = 0; if (unknown_user) { fputs ((char *) HDR_START, outfile); fputs ("\n", outfile); fputs ((char *) FROM, outfile); fputs ((char *) UNKNOWN_USER_NAME, outfile); fputs ("\n", outfile); fputs ((char *) UNKNOWN_USER, outfile); fputs ("\n", outfile); fputs ((char *) HDR_END, outfile); fputs ("\n", outfile); } } in_obj = 1; end_obj = 0; count_lines++; } else { end_obj = in_obj; in_obj = 0; } if (in_obj && !src_seen) src_seen = (int) (0 == regexec (&cookieinsre, curline, (size_t) 0, NULL, 0)); /* printf ("(end_obj, src_seen)=(%d,%d):%s", end_obj, src_seen, curline); */ if (0 == regexec (&passwdre, curline, 3, mailfromrm, 0)) { if (mailfromrm[1].rm_so != -1) { if (mailfromrm[2].rm_so != -1) whichoffset = 2; else whichoffset = 1; /* enclose passwd in '"'s; it can have spaces in it :( */ obj_len = mailfromrm[whichoffset].rm_eo - mailfromrm[1].rm_so; if (obj_len >= SMALLSTRBUFLEN - 3) { trace(ERROR, tr, "Passord \"%s\" exceeds maximum length of %d.\n", curline, SMALLSTRBUFLEN - 3); fclose(outfile); unlink(outfn); return(-1); } passwd[0] = '"'; memcpy ((passwd + 1), (char *) (curline + mailfromrm[1].rm_so), (size_t) (obj_len)); passwd[obj_len + 1] = '"'; passwd[obj_len + 2] = '\0'; } in_obj = prev; continue; } if (end_obj) { if (src_seen && passwd[0] != '\0') { fprintf (outfile, "password: %s\n", passwd); /* Don't log users' password unlesss debugging */ /* trace (NORM, tr, "Admincookies found password: (%s)\n", passwd); */ } if (email_info[0] != '\0') fputs (email_info, outfile); } fputs (curline, outfile); } } /* fgets */ /* empty submission, make a legal header for notify routine */ if (count_lines == 0) { fputs ((char *) HDR_START, outfile); fputs ("\n", outfile); fputs (email_info, outfile); fputs ((char *) HDR_END, outfile); fputs ("\n", outfile); } regfree (&messidre); regfree (&mailfromre); regfree (&mailreplytore); regfree (&mailfromncre); regfree (&subjre); regfree (&datere); regfree (&blanklinere); regfree (&cookieinsre); regfree (&passwdre); fclose (outfile); fclose (infile); if (unknown_user) return 0; return count_lines; }