Index: lukemftpd.h =================================================================== RCS file: /home/ncvs/src/contrib/lukemftpd/lukemftpd.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 lukemftpd.h --- lukemftpd.h 19 Jul 2001 16:25:06 -0000 1.1.1.1 +++ lukemftpd.h 28 Nov 2001 05:34:07 -0000 @@ -33,6 +33,9 @@ #include #include #include +#ifdef USE_PAM +#include /* XXX */ +#endif #include #include #include @@ -45,6 +48,10 @@ #include #include #include + +#ifdef USE_PAM +#include +#endif #if HAVE_DIRENT_H # include Index: src/ftpd.c =================================================================== RCS file: /home/ncvs/src/contrib/lukemftpd/src/ftpd.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 ftpd.c --- src/ftpd.c 19 Jul 2001 16:25:07 -0000 1.1.1.1 +++ src/ftpd.c 28 Nov 2001 05:34:10 -0000 @@ -166,6 +166,15 @@ int swaitmax = SWAITMAX; int swaitint = SWAITINT; +#ifdef USE_PAM +static int auth_pam (struct passwd**, const char*); +pam_handle_t *pamh = NULL; + +/* Kluge because the conversation mechanism has not been threshed out */ +static struct opie opiedata; +static char opieprompt[OPIE_CHALLENGE_MAX+1]; +#endif + static int bind_pasv_addr(void); static int checkuser(const char *, const char *, int, int, char **); static int checkaccess(const char *); @@ -603,7 +612,15 @@ myskey ? myskey : "error getting challenge", name); } else #endif +#ifndef USE_PAM reply(331, "Password required for %s.", name); +#else + /* XXX Kluge! The conversation mechanism needs to be fixed. */ + if (opiechallenge(&opiedata, (char *)name, opieprompt) == 0) + reply(331, "Response to %s required for %s.", opieprompt, name); + else + reply(331, "Password required for %s.", name); +#endif askpasswd = 1; /* @@ -776,6 +793,9 @@ static void end_login(void) { +#ifdef USE_PAM + int e; +#endif if (logged_in) { #ifdef NO_UTMP @@ -795,8 +815,166 @@ gidcount = 0; curclass.type = CLASS_REAL; (void) seteuid((uid_t)0); +#ifdef USE_PAM + if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); + if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + pamh = NULL; +#endif +} + +#ifdef USE_PAM + +/* + * the following code is stolen from imap-uw PAM authentication module and + * login.c + */ +#define COPY_STRING(s) (s ? strdup(s) : NULL) + +struct cred_t { + const char *uname; /* user name */ + const char *pass; /* password */ +}; +typedef struct cred_t cred_t; + +static int +auth_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata) +{ + int i; + cred_t *cred = (cred_t *) appdata; + struct pam_response *reply = + malloc(sizeof(struct pam_response) * num_msg); + + for (i = 0; i < num_msg; i++) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_ON: /* assume want user name */ + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = COPY_STRING(cred->uname); + /* PAM frees resp. */ + break; + case PAM_PROMPT_ECHO_OFF: /* assume want password */ + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = COPY_STRING(cred->pass); + /* PAM frees resp. */ + break; + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = NULL; + break; + default: /* unknown message style */ + free(reply); + return PAM_CONV_ERR; + } + } + + *resp = reply; + return PAM_SUCCESS; } +/* + * Attempt to authenticate the user using PAM. Returns 0 if the user is + * authenticated, or 1 if not authenticated. If some sort of PAM system + * error occurs (e.g., the "/etc/pam.conf" file is missing) then this + * function returns -1. This can be used as an indication that we should + * fall back to a different authentication mechanism. + */ +static int +auth_pam(struct passwd **ppw, const char *pass) +{ + pam_handle_t *pamh = NULL; + const char *tmpl_user; + const void *item; + int rval; + int e; + cred_t auth_cred = { (*ppw)->pw_name, pass }; + struct pam_conv conv = { &auth_conv, &auth_cred }; + + e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); + return -1; + } + + e = pam_set_item(pamh, PAM_RHOST, remotehost); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", + pam_strerror(pamh, e)); + return -1; + } + + e = pam_authenticate(pamh, 0); + switch (e) { + case PAM_SUCCESS: + /* + * With PAM we support the concept of a "template" + * user. The user enters a login name which is + * authenticated by PAM, usually via a remote service + * such as RADIUS or TACACS+. If authentication + * succeeds, a different but related "template" name + * is used for setting the credentials, shell, and + * home directory. The name the user enters need only + * exist on the remote authentication server, but the + * template name must be present in the local password + * database. + * + * This is supported by two various mechanisms in the + * individual modules. However, from the application's + * point of view, the template user is always passed + * back as a changed value of the PAM_USER item. + */ + if ((e = pam_get_item(pamh, PAM_USER, &item)) == + PAM_SUCCESS) { + tmpl_user = (const char *) item; + if (strcmp((*ppw)->pw_name, tmpl_user) != 0) + *ppw = getpwnam(tmpl_user); + } else + syslog(LOG_ERR, "Couldn't get PAM_USER: %s", + pam_strerror(pamh, e)); + rval = 0; + break; + + case PAM_AUTH_ERR: + case PAM_USER_UNKNOWN: + case PAM_MAXTRIES: + rval = 1; + break; + + default: + syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); + rval = -1; + break; + } + + if (rval == 0) { + e = pam_acct_mgmt(pamh, 0); + if (e == PAM_NEW_AUTHTOK_REQD) { + e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_chauthtok: %s", + pam_strerror(pamh, e)); + rval = 1; + } + } else if (e != PAM_SUCCESS) { + rval = 1; + } + } + + if (rval != 0) { + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + } + pamh = NULL; + } + return rval; +} + +#endif /* USE_PAM */ + void pass(const char *passwd) { @@ -805,6 +983,9 @@ char *class, root[MAXPATHLEN]; char *p; int len; +#ifdef USE_PAM + int e; +#endif class = NULL; if (logged_in || askpasswd == 0) { @@ -844,6 +1025,11 @@ } } #endif +#ifdef USE_PAM + rval = auth_pam(&pw, passwd); + if (rval >= 0) + goto skip; +#endif if (!sflag) rval = checkpassword(pw, passwd); else @@ -908,6 +1094,17 @@ (void) initgroups(pw->pw_name, pw->pw_gid); /* cache groups for cmds.c::matchgroup() */ gidcount = getgroups(sizeof(gidlist), gidlist); + +#ifdef USE_PAM + if (pamh) { + if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", + pam_strerror(pamh, e)); + } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); + } + } +#endif /* open wtmp before chroot */ #ifdef NO_UTMP