From owner-freebsd-bugs Tue Sep 4 3:30:20 2001 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by hub.freebsd.org (Postfix) with ESMTP id 5EE3737B408 for ; Tue, 4 Sep 2001 03:30:01 -0700 (PDT) Received: (from gnats@localhost) by freefall.freebsd.org (8.11.4/8.11.4) id f84AU1643371; Tue, 4 Sep 2001 03:30:01 -0700 (PDT) (envelope-from gnats) Received: from mailout04.sul.t-online.de (mailout04.sul.t-online.com [194.25.134.18]) by hub.freebsd.org (Postfix) with ESMTP id E89B137B409; Tue, 4 Sep 2001 03:27:28 -0700 (PDT) Received: from fwd00.sul.t-online.de by mailout04.sul.t-online.de with smtp id 15eDQV-0004QD-07; Tue, 04 Sep 2001 12:27:27 +0200 Received: from theater.dyndns.org (320068889749-0001@[217.82.195.91]) by fmrl00.sul.t-online.com with esmtp id 15eDQJ-1Ud0QiC; Tue, 4 Sep 2001 12:27:15 +0200 Received: from monster.ikea.net (monster.ikea.net [192.168.2.3]) by theater.dyndns.org (8.11.4/8.11.3) with ESMTP id f84ASeh91134; Tue, 4 Sep 2001 12:28:41 +0200 (CEST) (envelope-from vs@foldr.org) Received: (from vs@localhost) by monster.ikea.net (8.11.6/8.11.6) id f84ARDZ01483; Tue, 4 Sep 2001 12:27:13 +0200 (CEST) (envelope-from vs) Message-Id: <200109041027.f84ARDZ01483@monster.ikea.net> Date: Tue, 4 Sep 2001 12:27:13 +0200 (CEST) From: Volker Stolz To: FreeBSD-gnats-submit@freebsd.org Cc: markm@freebsd.org X-Send-Pr-Version: 3.113 Subject: bin/30304: [PATCH] PAM authentication for uucpd Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >Number: 30304 >Category: bin >Synopsis: [PATCH] PAM authentication for uucpd >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Sep 04 03:30:01 PDT 2001 >Closed-Date: >Last-Modified: >Originator: Volker Stolz >Release: FreeBSD 4.4-RC i386 >Organization: Lehrstuhl für Informatik II >Environment: System: FreeBSD monster.ikea.net 4.4-RC FreeBSD 4.4-RC #0: Thu Aug 30 19:27:52 CEST 2001 vs@monster.ikea.net:/opt/obj/opt/src/sys/MONSTER i386 >Description: [Resend because of mailer trouble on my end which seems to have dumped my previous PR] uucpd only uses getpwnam() for authentication. This patch adds PAM auth via facility "uucpd" in /etc/pam.conf, e.g. uucp auth sufficient pam_skey.so uucp auth sufficient pam_ssh.so try_first_pass uucp auth requisite pam_cleartext_pass_ok.so uucp auth required pam_unix.so try_first_pass uucp account required pam_unix.so uucp password required pam_permit.so uucp session required pam_permit.so >How-To-Repeat: n/a >Fix: http://www-i2.informatik.rwth-aachen.de/~stolz/FreeBSD/uucpd-pam.patch --- uucpd/Makefile Wed Apr 25 12:41:03 2001 +++ vs.uucpd/Makefile Thu Aug 30 20:58:39 2001 @@ -5,4 +5,11 @@ MAN= uucpd.8 LDADD= -lcrypt -lutil DPADD= ${LIBCRYPT} ${LIBUTIL} + +.if !defined(NOPAM) +CFLAGS+= -DUSE_PAM +DPADD+= ${LIBPAM} +LDADD+= ${MINUSLPAM} +.endif + .include --- uucpd/uucpd.c Sat Jun 3 19:00:56 2000 +++ vs.uucpd/uucpd.c Tue Sep 4 11:16:02 2001 @@ -53,12 +53,14 @@ * uucico's TCP channel causes this server to be run at the remote end. */ -#include +#include +#include +#include #include #include #include -#include #include + #include #include #include @@ -72,9 +74,39 @@ #include #include #include -#include + #include "pathnames.h" +#ifdef USE_PAM +#include +#include +#endif /* USE_PAM */ + +#ifdef USE_PAM +static int auth_pam __P((void)); + +static pam_handle_t *pamh = NULL; +static char **environ_pam; + +#define PAM_END { \ + 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)); \ +} + +struct cred_t { + const char *uname; /* user name */ + const char *pass; /* password */ +}; +typedef struct cred_t cred_t; + +#define COPY_STRING(s) (s ? strdup(s) : NULL) + +#endif /* USE_PAM */ + #if (MAXLOGNAME-1) > UT_NAMESIZE #define LOGNAMESIZE UT_NAMESIZE #else @@ -94,9 +126,16 @@ Logname, NULL, }; + +char user[64]; +char passwd[64]; + extern char **environ; extern void logwtmp(char *line, char *name, char *host); +struct passwd *pw; +char remotehost[MAXHOSTNAMELEN]; + void doit(struct sockaddr *sinp); void dologout(void); int readline(char start[], int num, int passw); @@ -104,26 +143,30 @@ int main(int argc, char **argv) { + struct sockaddr *sin; + environ = nenv; close(1); close(2); dup(0); dup(0); hisaddrlen = sizeof (hisctladdr); openlog("uucpd", LOG_PID, LOG_DAEMON); - if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) { + + sin = (struct sockaddr *)&hisctladdr; + if (getpeername(0, sin, &hisaddrlen) < 0) { syslog(LOG_ERR, "getpeername: %m"); _exit(1); } - doit((struct sockaddr *)&hisctladdr); + + realhostname_sa(remotehost, sizeof(remotehost) - 1, sin, sin->sa_len); + remotehost[sizeof(remotehost) - 1] = '\0'; + + doit(sin); dologout(); exit(0); } -void badlogin(char *name, struct sockaddr *sin) +void badlogin(char *name) { - char remotehost[MAXHOSTNAMELEN]; - - realhostname_sa(remotehost, sizeof(remotehost) - 1, sin, sin->sa_len); - remotehost[sizeof(remotehost) - 1] = '\0'; syslog(LOG_NOTICE, "LOGIN FAILURE FROM %s", remotehost); syslog(LOG_AUTHPRIV|LOG_NOTICE, @@ -135,67 +178,256 @@ void doit(struct sockaddr *sinp) { - char user[64], passwd[64]; - char *xpasswd, *crypt(); - struct passwd *pw; pid_t s; - int pwdok =0; + int pwdok = 0; + int rval = -1; /* Assume PAM failed */ + int e; /* PAM rc */ alarm(60); do { - printf("login: "); fflush(stdout); - errno = 0; - if (readline(user, sizeof user, 0) < 0) { - syslog(LOG_WARNING, "login read: %m"); - _exit(1); - } + printf("login: "); fflush(stdout); + errno = 0; + if (readline(user, sizeof user, 0) < 0) { + syslog(LOG_WARNING, "login read: %m"); + _exit(1); + } } while (user[0] == '\0'); + /* truncate username to LOGNAMESIZE characters */ user[LOGNAMESIZE] = '\0'; - pw = getpwnam(user); - /* - * Fail after password if: - * 1. Invalid user - * 2. Shell is not uucico - * 3. Account has expired - * 4. Password is incorrect - */ - if (pw != NULL && strcmp(pw->pw_shell, _PATH_UUCICO) == 0 && - (!pw->pw_expire || time(NULL) >= pw->pw_expire)) - pwdok = 1; + /* always ask for passwords to deter account guessing */ - if (!pwdok || (pw->pw_passwd && *pw->pw_passwd != '\0')) { - printf("Password: "); fflush(stdout); - errno = 0; - if (readline(passwd, sizeof passwd, 1) < 0) { - syslog(LOG_WARNING, "passwd for '%s' read: %m", user); - _exit(1); - } - if (pwdok) { - xpasswd = crypt(passwd, pw->pw_passwd); - if (strcmp(xpasswd, pw->pw_passwd)) - pwdok = 0; - } - if (!pwdok) - badlogin(user, sinp); + printf("Password: "); fflush(stdout); + errno = 0; + if (readline(passwd, sizeof passwd, 1) < 0) { + syslog(LOG_WARNING, "passwd for '%s' read: %m", user); + _exit(1); } alarm(0); + + /* pw might get changed by auth_pam */ + pw = getpwnam(user); + +#ifdef USE_PAM + /* + * Try to authenticate using PAM. If a PAM system error + * occurs, perhaps because of a botched configuration, + * then fall back to using traditional Unix authentication. + */ + (void)setpriority(PRIO_PROCESS, 0, -4); + rval = auth_pam(); + (void)setpriority(PRIO_PROCESS, 0, 0); + +#endif /* USE_PAM */ + + + /* + * Fail after password if: + * 1. Invalid user + * 2. Shell is not uucico + * 3. Account has expired + * 4. Password is incorrect + */ + + if (pw != NULL) { + if (rval == -1) /* PAM bailed out */ + pwdok = (strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) == 0); + else + pwdok = (rval == 0); + + pwdok = pwdok && (*pw->pw_passwd != '\0') + && (strcmp(pw->pw_shell, _PATH_UUCICO) == 0) + && (!(pw->pw_expire && (time(NULL) >= pw->pw_expire))); + } + + if (!pwdok) + badlogin(user); + sprintf(Username, "USER=%s", pw->pw_name); sprintf(Logname, "LOGNAME=%s", pw->pw_name); if ((s = fork()) < 0) { syslog(LOG_ERR, "fork: %m"); +#ifdef USE_PAM + PAM_END; +#endif _exit(1); } else if (s == 0) { - dologin(pw, sinp); - setgid(pw->pw_gid); - initgroups(pw->pw_name, pw->pw_gid); - chdir(pw->pw_dir); - setuid(pw->pw_uid); - execl(pw->pw_shell, "uucico", NULL); - syslog(LOG_ERR, "execl: %m"); - _exit(1); + dologin(pw, sinp); + setgid(pw->pw_gid); + initgroups(pw->pw_name, pw->pw_gid); + chdir(pw->pw_dir); + setuid(pw->pw_uid); + +#ifdef USE_PAM + if (pamh) { + if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) + != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", + pam_strerror(pamh, e)); + } else if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", + pam_strerror(pamh, e)); + } + /* Tell PAM that our parent cares for us */ + if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS) + syslog(LOG_ERR, "pam_end: %s", + pam_strerror(pamh, e)); + } +#endif /* USE_PAM_ */ + + execl(pw->pw_shell, "uucico", NULL); + syslog(LOG_ERR, "execl: %m"); + _exit(1); + } else { + /* parent - wait for child to finish, then cleanup + session */ + wait(NULL); +#ifdef USE_PAM + PAM_END; +#endif + exit(0); + } +} + + +#ifdef USE_PAM +/* + * PAM conv stolen from ftpd.c. We already got the password ourselves, + * as most people wouldn't expect the UUCP login prompt to change. + */ + +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() +{ + const char *tmpl_user; + const void *item; + int rval; + int e; + + cred_t auth_cred = { pw->pw_name, passwd }; + struct pam_conv conv = { &auth_conv, &auth_cred }; + + if ((e = pam_start("uucp", user, &conv, &pamh)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); + return -1; + } + + if (remotehost != NULL && + (e = pam_set_item(pamh, PAM_RHOST, remotehost)) != 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(user, tmpl_user) != 0) + pw = 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 */ int readline(char start[], int num, int passw) { >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message