summaryrefslogtreecommitdiff
path: root/poppassd.c
diff options
context:
space:
mode:
authorAdam Conrad <adconrad@0c3.net>2005-01-21 01:35:21 -0800
committerAdam Conrad <adconrad@0c3.net>2005-01-21 01:35:21 -0800
commit41cae64487170058599237d290a2d0f59d50a539 (patch)
tree303d1540d5dcacfb81fc2edf2c6cbaf2ffa10b91 /poppassd.c
Import poppassd_1.8.5.orig.tar.gz
[dgit import orig poppassd_1.8.5.orig.tar.gz]
Diffstat (limited to 'poppassd.c')
-rw-r--r--poppassd.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/poppassd.c b/poppassd.c
new file mode 100644
index 0000000..b9113de
--- /dev/null
+++ b/poppassd.c
@@ -0,0 +1,273 @@
+/*
+ * poppassd.c
+ *
+ * A Eudora and NUPOP change password server.
+ *
+ * Pawel Krawczyk <kravietz@echelon.pl>
+ *
+ * Based on poppassd by John Norstad <j-norstad@nwu.edu>,
+ * Roy Smith <roy@nyu.edu> and Daniel L. Leavitt <dll.mitre.org>.
+ * Shadow file update code taken from shadow-960810 by John F. Haugh
+ * II <jfh@rpp386.cactus.org> and Marek Michalkiewicz
+ * <marekm@i17linuxb.ists.pwr.wroc.pl>
+ *
+ * See README for more information.
+ */
+
+/* Steve Dorner's description of the simple protocol:
+ *
+ * The server's responses should be like an FTP server's responses;
+ * 1xx for in progress, 2xx for success, 3xx for more information
+ * needed, 4xx for temporary failure, and 5xx for permanent failure.
+ * Putting it all together, here's a sample conversation:
+ *
+ * S: 200 hello\r\n
+ * E: user yourloginname\r\n
+ * S: 300 please send your password now\r\n
+ * E: pass yourcurrentpassword\r\n
+ * S: 200 My, that was tasty\r\n
+ * E: newpass yournewpassword\r\n
+ * S: 200 Happy to oblige\r\n
+ * E: quit\r\n
+ * S: 200 Bye-bye\r\n
+ * S: <closes connection>
+ * E: <closes connection>
+ */
+
+#define VERSION "1.8.5"
+#define BAD_PASS_DELAY 3 /* delay in seconds after bad 'Old password' */
+#define POP_MIN_UID 100 /* minimum UID which is allowed to change
+ password via poppassd */
+
+/* These need to be quoted because they are only used as
+ * parts of format strings for sscanf; actual lengths are smaller
+ * by 5 because the buffer needs to fit "user " and "pass " strings
+ * as well
+ */
+#define MAX_LEN_USERNAME "64" /* maximum length of username buffer */
+#define MAX_LEN_PASS "128" /* maximum length of password buffer */
+
+#define SUCCESS 1
+#define FAILURE 0
+#define BUFSIZE 1000
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <strings.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <string.h>
+#include <dirent.h>
+#include <shadow.h>
+#include <signal.h>
+#include <sys/time.h>
+#if !__GLIBC__ >= 2
+#include <ulimit.h>
+#endif
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+#define LOCK_TRIES 30
+
+#ifndef UL_SETFSIZE
+#ifdef UL_SFILLIM
+#define UL_SETFSIZE UL_SFILLIM
+#else
+#define UL_SETFSIZE 2
+#endif
+#endif
+
+/* need to be global for poppassd_conv */
+char oldpass[BUFSIZE];
+char newpass[BUFSIZE];
+#define POP_OLDPASS 0
+#define POP_NEWPASS 1
+#define POP_SKIPASS 2
+short int pop_state = POP_OLDPASS;
+
+void WriteToClient (char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ vfprintf (stdout, fmt, ap);
+ fputs ("\r\n", stdout );
+ fflush (stdout);
+ va_end (ap);
+}
+
+void ReadFromClient (char *line)
+{
+ char *sp;
+ int i;
+
+ bzero(line, BUFSIZE);
+ fgets (line, BUFSIZE-1, stdin);
+ if ((sp = strchr(line, '\n')) != NULL) *sp = '\0';
+ if ((sp = strchr(line, '\r')) != NULL) *sp = '\0';
+
+ /* convert initial keyword on line to lower case. */
+
+ for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp);
+ line[BUFSIZE-1] = '\0';
+}
+
+int poppassd_conv(num_msg, msg, resp, appdata_ptr)
+ int num_msg;
+ const struct pam_message **msg;
+ struct pam_response **resp;
+ void *appdata_ptr;
+{
+ int i;
+ struct pam_response *r = malloc(sizeof(struct pam_response) * num_msg);
+
+ if(num_msg <= 0)
+ return(PAM_CONV_ERR);
+
+ if(r == NULL)
+ return(PAM_CONV_ERR);
+
+ for(i=0; i<num_msg; i++) {
+ if(msg[i]->msg_style == PAM_ERROR_MSG) {
+ WriteToClient ("500 PAM error: %s", msg[i]->msg);
+ syslog(LOG_ERR, "PAM error: %s", msg[i]->msg);
+ /*
+ * If there is an error, we don't want to be sending in
+ * anything more, so set pop_state to invalid
+ */
+ pop_state = POP_SKIPASS;
+ }
+
+ r[i].resp_retcode = 0;
+ if(msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
+ msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+ {
+ switch(pop_state) {
+ case POP_OLDPASS: r[i].resp = strdup(oldpass);
+ break;
+ case POP_NEWPASS: r[i].resp = strdup(newpass);
+ break;
+ case POP_SKIPASS: r[i].resp = NULL;
+ break;
+ default: syslog(LOG_ERR, "PAM error: too many switches (state=%d)", pop_state);
+ }
+ } else
+ {
+ r[i].resp = strdup("");
+ }
+ }
+
+ *resp = r;
+ return PAM_SUCCESS;
+}
+
+const struct pam_conv pam_conv = {
+ poppassd_conv,
+ NULL
+};
+
+int main (int argc, char *argv[])
+{
+ char line[BUFSIZE];
+ char user[BUFSIZE];
+ char emess[BUFSIZE];
+ char *slavedev;
+ struct passwd *pw, *getpwnam();
+ struct spwd *sp;
+ int c, master;
+ pid_t pid, wpid;
+ int wstat;
+ int ret;
+ pam_handle_t *pamh=NULL;
+ char *item=oldpass;
+
+ *user = *oldpass = *newpass = 0;
+
+ openlog("poppassd", LOG_PID, LOG_LOCAL4);
+
+ WriteToClient ("200 poppassd v%s hello, who are you?", VERSION);
+ ReadFromClient (line);
+ if( strlen(line) > atoi(MAX_LEN_USERNAME) ) {
+ WriteToClient ("500 Username too long (max %d).", atoi(MAX_LEN_USERNAME));
+ exit(1);
+ }
+ sscanf (line, "user %" MAX_LEN_USERNAME "s", user) ;
+ if (strlen (user) == 0 )
+ {
+ WriteToClient ("500 Username required.");
+ exit(1);
+ }
+ if(pam_start("poppassd", user, &pam_conv, &pamh) != PAM_SUCCESS)
+ {
+ WriteToClient("500 Invalid username.");
+ exit(1);
+ }
+
+ WriteToClient ("200 Your password please.");
+ ReadFromClient (line);
+ if( strlen(line) > atoi(MAX_LEN_PASS) )
+ {
+ WriteToClient("500 Password length exceeded (max %d).",
+ atoi(MAX_LEN_PASS) );
+ exit(1);
+ }
+ sscanf (line, "pass %" MAX_LEN_PASS "c", oldpass);
+ if(strlen(oldpass) == 0)
+ {
+ WriteToClient("500 Password required.");
+ exit(1);
+ }
+ if(pam_authenticate(pamh, 0) != PAM_SUCCESS)
+ {
+ WriteToClient ("500 Old password is incorrect.");
+ syslog(LOG_ERR, "old password is incorrect for user %s", user);
+ /* pause to make brute force attacks harder */
+ sleep(BAD_PASS_DELAY);
+ exit(1);
+ }
+
+ pw=getpwnam(user);
+
+ if(pw->pw_uid<POP_MIN_UID || pw == NULL) {
+ WriteToClient("500 Old password is incorrect.");
+ syslog(LOG_ERR, "failed attempt to change password for %s", user);
+ exit(1);
+ }
+
+ pop_state = POP_NEWPASS;
+
+ WriteToClient ("200 Your new password please.");
+ ReadFromClient (line);
+ sscanf (line, "newpass %" MAX_LEN_PASS "c", newpass);
+
+ /* new pass required */
+ if (strlen (newpass) == 0)
+ {
+ WriteToClient ("500 New password required.");
+ exit(1);
+ }
+
+ if(pam_chauthtok(pamh, 0) != PAM_SUCCESS) {
+ WriteToClient("500 Server error, password not changed");
+ exit(1);
+ } else {
+ syslog(LOG_ERR, "changed POP3 password for %s", user);
+ WriteToClient("200 Password changed, thank-you.");
+ ReadFromClient (line);
+ if (strncmp(line, "quit", 4) != 0) {
+ WriteToClient("500 Quit required.");
+ exit (1);
+ }
+ }
+
+ pam_end(pamh, 0);
+ WriteToClient("200 Bye.");
+ closelog();
+ exit(0);
+ }
+