Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 4 Sep 2001 12:27:13 +0200 (CEST)
From:      Volker Stolz <vs@foldr.org>
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        markm@freebsd.org
Subject:   bin/30304: [PATCH] PAM authentication for uucpd
Message-ID:  <200109041027.f84ARDZ01483@monster.ikea.net>

next in thread | raw e-mail | index | archive | help

>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 <bsd.prog.mk>
--- 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 <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
 #include <sys/wait.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
-#include <sys/param.h>
 #include <netinet/in.h>
+
 #include <arpa/inet.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -72,9 +74,39 @@
 #include <time.h>
 #include <unistd.h>
 #include <utmp.h>
-#include <sys/param.h>
+
 #include "pathnames.h"
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200109041027.f84ARDZ01483>