int changeuser(POP *p, struct passwd *pwd) { if(setgid(pwd->pw_gid) < 0) { pop_log (p, POP_PRIORITY, "Unable to change to gid %u: %s", (unsigned)pwd->pw_gid, strerror(errno)); return pop_msg (p, POP_FAILURE, "Unable to change gid"); } if(setuid(pwd->pw_uid) < 0) { pop_log (p, POP_PRIORITY, "Unable to change to uid %u: %s", (unsigned)pwd->pw_uid, strerror(errno)); return pop_msg (p, POP_FAILURE, "Unable to change uid"); } #ifdef DEBUG if(p->debug) pop_log(p, POP_DEBUG,"uid = %u, gid = %u", (unsigned)getuid(), (unsigned)getgid()); #endif /* DEBUG */ return POP_SUCCESS; }
int login_user(POP *p) { struct stat st; struct passwd *pw; /* Look for the user in the password file */ if ((pw = k_getpwnam(p->user)) == NULL) { pop_log(p, POP_PRIORITY, "user %s (from %s) not found", p->user, p->ipaddr); return pop_msg(p, POP_FAILURE, "Login incorrect."); } pop_log(p, POP_INFO, "login from %s as %s", p->ipaddr, p->user); /* Build the name of the user's maildrop */ snprintf(p->drop_name, sizeof(p->drop_name), "%s/%s", POP_MAILDIR, p->user); if(stat(p->drop_name, &st) < 0 || !S_ISDIR(st.st_mode)){ /* Make a temporary copy of the user's maildrop */ /* and set the group and user id */ if (pop_dropcopy(p, pw) != POP_SUCCESS) return (POP_FAILURE); /* Get information about the maildrop */ if (pop_dropinfo(p) != POP_SUCCESS) return(POP_FAILURE); } else { if(changeuser(p, pw) != POP_SUCCESS) return POP_FAILURE; if(pop_maildir_info(p) != POP_SUCCESS) return POP_FAILURE; } /* Initialize the last-message-accessed number */ p->last_msg = 0; return POP_SUCCESS; }
int add_missing_headers(POP *p, MsgInfoList *mp) { #if defined(UIDL) || defined(XOVER) if (mp->msg_id == NULL) { if (asprintf(&mp->msg_id, "no-message-id-%d", mp->number) == -1) { fclose (p->drop); p->msg_count = 0; return pop_msg (p,POP_FAILURE, "Can't build message list for '%s': Out of memory", p->user); } } #endif #ifdef XOVER if (mp->subject == NULL) mp->subject = "<none>"; if (mp->from == NULL) mp->from = "<unknown>"; if (mp->date == NULL) mp->date = "<unknown>"; #endif return POP_SUCCESS; }
int pop_updt (POP *p) { FILE * md; /* Stream pointer for the user's maildrop */ int mfd; /* File descriptor for above */ char buffer[BUFSIZ]; /* Read buffer */ MsgInfoList * mp; /* Pointer to message info list */ int msg_num; /* Current message counter */ int status_written; /* Status header field written */ int nchar; /* Bytes read/written */ long offset; /* New mail offset */ int blank_line; #ifdef DEBUG if (p->debug) { pop_log(p,POP_DEBUG,"Performing maildrop update..."); pop_log(p,POP_DEBUG,"Checking to see if all messages were deleted"); } #endif /* DEBUG */ if(IS_MAILDIR(p)) return pop_maildir_update(p); if (p->msgs_deleted == p->msg_count) { /* Truncate before close, to avoid race condition, DO NOT UNLINK! Another process may have opened, and not yet tried to lock */ ftruncate ((int)fileno(p->drop),0); fclose(p->drop) ; return (POP_SUCCESS); } #ifdef DEBUG if (p->debug) pop_log(p,POP_DEBUG,"Opening mail drop \"%s\"",p->drop_name); #endif /* DEBUG */ /* Open the user's real maildrop */ if ((mfd = open(p->drop_name,O_RDWR|O_CREAT,0600)) == -1 || (md = fdopen(mfd,"r+")) == NULL) { return pop_msg(p,POP_FAILURE,standard_error); } /* Lock the user's real mail drop */ if ( flock(mfd, LOCK_EX) == -1 ) { fclose(md) ; return pop_msg(p,POP_FAILURE, "flock: '%s': %s", p->temp_drop, strerror(errno)); } /* Go to the right places */ offset = lseek((int)fileno(p->drop),0,SEEK_END) ; /* Append any messages that may have arrived during the session to the temporary maildrop */ while ((nchar=read(mfd,buffer,BUFSIZ)) > 0) if ( nchar != write((int)fileno(p->drop),buffer,nchar) ) { nchar = -1; break ; } if ( nchar != 0 ) { fclose(md) ; ftruncate((int)fileno(p->drop),(int)offset) ; fclose(p->drop) ; return pop_msg(p,POP_FAILURE,standard_error); } rewind(md); lseek(mfd,0,SEEK_SET); ftruncate(mfd,0) ; /* Synch stdio and the kernel for the POP drop */ rewind(p->drop); lseek((int)fileno(p->drop),0,SEEK_SET); /* Transfer messages not flagged for deletion from the temporary maildrop to the new maildrop */ #ifdef DEBUG if (p->debug) pop_log(p,POP_DEBUG,"Creating new maildrop \"%s\" from \"%s\"", p->drop_name,p->temp_drop); #endif /* DEBUG */ for (msg_num = 0; msg_num < p->msg_count; ++msg_num) { int doing_body; /* Get a pointer to the message information list */ mp = &p->mlp[msg_num]; if (mp->flags & DEL_FLAG) { #ifdef DEBUG if(p->debug) pop_log(p,POP_DEBUG, "Message %d flagged for deletion.",mp->number); #endif /* DEBUG */ continue; } fseek(p->drop,mp->offset,0); #ifdef DEBUG if(p->debug) pop_log(p,POP_DEBUG,"Copying message %d.",mp->number); #endif /* DEBUG */ blank_line = 1; for(status_written = doing_body = 0 ; fgets(buffer,MAXMSGLINELEN,p->drop);) { if (doing_body == 0) { /* Header */ /* Update the message status */ if (strncasecmp(buffer,"Status:",7) == 0) { if (mp->flags & RETR_FLAG) fputs("Status: RO\n",md); else fputs(buffer, md); status_written++; continue; } /* A blank line signals the end of the header. */ if (*buffer == '\n') { doing_body = 1; if (status_written == 0) { if (mp->flags & RETR_FLAG) fputs("Status: RO\n\n",md); else fputs("Status: U\n\n",md); } else fputs ("\n", md); continue; } /* Save another header line */ fputs (buffer, md); } else { /* Body */ if (blank_line && strncmp(buffer,"From ",5) == 0) break; fputs (buffer, md); blank_line = (*buffer == '\n'); } } } /* flush and check for errors now! The new mail will writen without stdio, since we need not separate messages */ fflush(md) ; if (ferror(md)) { ftruncate(mfd,0) ; fclose(md) ; fclose(p->drop) ; return pop_msg(p,POP_FAILURE,standard_error); } /* Go to start of new mail if any */ lseek((int)fileno(p->drop),offset,SEEK_SET); while((nchar=read((int)fileno(p->drop),buffer,BUFSIZ)) > 0) if ( nchar != write(mfd,buffer,nchar) ) { nchar = -1; break ; } if ( nchar != 0 ) { ftruncate(mfd,0) ; fclose(md) ; fclose(p->drop) ; return pop_msg(p,POP_FAILURE,standard_error); } /* Close the maildrop and empty temporary maildrop */ fclose(md); ftruncate((int)fileno(p->drop),0); fclose(p->drop); return(pop_quit(p)); }
int pop_pass (POP *p) { struct passwd *pw; int i; int status; /* Make one string of all these parameters */ for (i = 1; i < p->parm_count; ++i) p->pop_parm[i][strlen(p->pop_parm[i])] = ' '; /* Look for the user in the password file */ if ((pw = k_getpwnam(p->user)) == NULL) return (pop_msg(p,POP_FAILURE, "Password supplied for \"%s\" is incorrect.", p->user)); if (p->kerberosp) { #ifdef KRB5 if (p->version == 5) { char *name; if (!krb5_kuserok (p->context, p->principal, p->user)) { pop_log (p, POP_PRIORITY, "krb5 permission denied"); return pop_msg(p, POP_FAILURE, "Popping not authorized"); } if(krb5_unparse_name (p->context, p->principal, &name) == 0) { pop_log(p, POP_INFO, "%s: %s -> %s", p->ipaddr, name, p->user); free (name); } } else { pop_log (p, POP_PRIORITY, "kerberos authentication failed"); return pop_msg (p, POP_FAILURE, "kerberos authentication failed"); } #endif { } } else { /* We don't accept connections from users with null passwords */ if (pw->pw_passwd == NULL) return (pop_msg(p, POP_FAILURE, "Password supplied for \"%s\" is incorrect.", p->user)); #ifdef OTP if (otp_verify_user (&p->otp_ctx, p->pop_parm[1]) == 0) /* pass OK */; else #endif /* Compare the supplied password with the password file entry */ if (p->auth_level != AUTH_NONE) return pop_msg(p, POP_FAILURE, "Password supplied for \"%s\" is incorrect.", p->user); else if (!strcmp(crypt(p->pop_parm[1], pw->pw_passwd), pw->pw_passwd)) /* pass OK */; else { int ret = -1; #ifdef KRB5 if(ret) ret = krb5_verify_password (p); #endif if(ret) return pop_msg(p, POP_FAILURE, "Password incorrect"); } } status = login_user(p); if(status != POP_SUCCESS) return status; /* Authorization completed successfully */ return (pop_msg (p, POP_SUCCESS, "%s has %d message(s) (%ld octets).", p->user, p->msg_count, p->drop_size)); }
/* * popper: Handle a Post Office Protocol version 3 session */ int main (int argc, char **argv) { POP p; state_table * s; char message[MAXLINELEN]; signal(SIGHUP, catchSIGHUP); signal(SIGPIPE, catchSIGHUP); /* Start things rolling */ pop_init(&p,argc,argv); /* Tell the user that we are listenting */ pop_msg(&p,POP_SUCCESS, "POP3 server ready"); /* State loop. The POP server is always in a particular state in which a specific suite of commands can be executed. The following loop reads a line from the client, gets the command, and processes it in the current context (if allowed) or rejects it. This continues until the client quits or an error occurs. */ for (p.CurrentState=auth1;p.CurrentState!=halt&&p.CurrentState!=error;) { if (hangup) { pop_msg(&p, POP_FAILURE, "POP hangup: %s", p.myhost); if (p.CurrentState > auth2 && !pop_updt(&p)) pop_msg(&p, POP_FAILURE, "POP mailbox update failed: %s", p.myhost); p.CurrentState = error; } else if (tgets(message, MAXLINELEN, p.input, pop_timeout) == NULL) { pop_msg(&p, POP_FAILURE, "POP timeout: %s", p.myhost); if (p.CurrentState > auth2 && !pop_updt(&p)) pop_msg(&p,POP_FAILURE, "POP mailbox update failed: %s", p.myhost); p.CurrentState = error; } else { /* Search for the command in the command/state table */ if ((s = pop_get_command(&p,message)) == NULL) continue; /* Call the function associated with this command in the current state */ if (s->function) p.CurrentState = s->result[(*s->function)(&p)]; /* Otherwise assume NOOP and send an OK message to the client */ else { p.CurrentState = s->success_state; pop_msg(&p,POP_SUCCESS,"time passes"); } } } /* Say goodbye to the client */ pop_msg(&p,POP_SUCCESS,"Pop server at %s signing off.",p.myhost); /* Log the end of activity */ pop_log(&p,POP_PRIORITY, "(v%s) Ending request from \"%s\" at %s\n",VERSION,p.client,p.ipaddr); /* Stop logging */ closelog(); return(0); }
int pop_dropinfo(POP *p) { char buffer[BUFSIZ]; /* Read buffer */ MsgInfoList * mp; /* Pointer to message info list */ int msg_num; /* Current message counter */ int nchar; /* Bytes written/read */ int blank_line = 1; /* previous line was blank */ int in_header = 0; /* if we are in a header block */ /* Initialize maildrop status variables in the POP parameter block */ p->msg_count = 0; p->msgs_deleted = 0; p->last_msg = 0; p->bytes_deleted = 0; p->drop_size = 0; /* Allocate memory for message information structures */ p->msg_count = ALLOC_MSGS; p->mlp = (MsgInfoList *)calloc((unsigned)p->msg_count,sizeof(MsgInfoList)); if (p->mlp == NULL){ fclose (p->drop); p->msg_count = 0; return pop_msg (p,POP_FAILURE, "Can't build message list for '%s': Out of memory", p->user); } rewind (p->drop); /* Scan the file, loading the message information list with information about each message */ for (msg_num = p->drop_size = 0, mp = p->mlp - 1; fgets(buffer,MAXMSGLINELEN,p->drop);) { nchar = strlen(buffer); if (blank_line && strncmp(buffer,"From ",5) == 0) { in_header = 1; if (++msg_num > p->msg_count) { p->mlp=(MsgInfoList *) realloc(p->mlp, (p->msg_count+=ALLOC_MSGS)*sizeof(MsgInfoList)); if (p->mlp == NULL){ fclose (p->drop); p->msg_count = 0; return pop_msg (p,POP_FAILURE, "Can't build message list for '%s': Out of memory", p->user); } mp = p->mlp + msg_num - 2; } ++mp; mp->number = msg_num; mp->length = 0; mp->lines = 0; mp->offset = ftell(p->drop) - nchar; mp->flags = 0; #if defined(UIDL) || defined(XOVER) mp->msg_id = 0; #endif #ifdef XOVER mp->subject = 0; mp->from = 0; mp->date = 0; #endif #ifdef DEBUG if(p->debug) pop_log(p, POP_DEBUG, "Msg %d at offset %ld being added to list", mp->number, mp->offset); #endif /* DEBUG */ } else if(in_header) parse_header(mp, buffer); blank_line = (strncmp(buffer, "\n", nchar) == 0); if(blank_line) { int e; in_header = 0; e = add_missing_headers(p, mp); if(e != POP_SUCCESS) return e; } mp->length += nchar; p->drop_size += nchar; mp->lines++; } p->msg_count = msg_num; #ifdef DEBUG if(p->debug && msg_num > 0) { int i; for (i = 0, mp = p->mlp; i < p->msg_count; i++, mp++) #ifdef UIDL pop_log(p,POP_DEBUG, "Msg %d at offset %ld is %ld octets long and has %u lines and id %s.", mp->number,mp->offset,mp->length,mp->lines, mp->msg_id); #else pop_log(p,POP_DEBUG, "Msg %d at offset %d is %d octets long and has %u lines.", mp->number,mp->offset,mp->length,mp->lines); #endif } #endif /* DEBUG */ return(POP_SUCCESS); }