Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 16 May 2002 15:45:25 -0500
From:      "Jacques A. Vidrine" <nectar@FreeBSD.org>
To:        freebsd-audit@FreeBSD.org
Subject:   Kerberos 5 and SU
Message-ID:  <20020516204525.GA26640@madman.nectar.cc>

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

--dDRMvlgZJXvWKvBx
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello,

Attached are two similar implementations of Kerberos 5 support for
su(1).  Why two?

 1. pam_ksu --- This is the long-term solution, but is only 
                appropriate for -CURRENT.  -STABLE does not have
                OpenPAM, and more importantly does not have a PAMified
                SU [1].

 2. su ---      This is for -STABLE only.  Support was added in
                parallel to the existing Kerberos 4 support.  This has
                made the code even more #ifdef grotty, but it will not
                be maintained after 4.x anyway.  The code path is
                still straightforward.

FYI, the `get_target_principal' functions in each correspond, and the
`auth_krb5' function in pam_ksu corresponds with the `kerberos5'
function in su(1).

Please tear it up :-)  I'm gone for the weekend.

Cheers,
-- 
Jacques A. Vidrine <n@nectar.cc>                 http://www.nectar.cc/
NTT/Verio SME          .     FreeBSD UNIX     .       Heimdal Kerberos
jvidrine@verio.net     .  nectar@FreeBSD.org  .          nectar@kth.se

[1] DES reports that he currently has no plans to backport the SU
    PAMificiation.

--dDRMvlgZJXvWKvBx
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="su.patch"

Index: Makefile
===================================================================
RCS file: /home/ncvs/src/usr.bin/su/Makefile,v
retrieving revision 1.29
diff -u -r1.29 Makefile
--- Makefile	2000/02/24 21:06:21	1.29
+++ Makefile	2002/05/16 20:42:46
@@ -14,10 +14,19 @@
 CFLAGS+= -Wall
 
 .if exists(${DESTDIR}${LIBDIR}/libkrb.a) && defined(MAKE_KERBEROS4)
-CFLAGS+=-DKERBEROS
+CFLAGS+=-DKERBEROS4
 DPADD+=	${LIBKRB} ${LIBCRYPTO} ${LIBCOM_ERR}
 LDADD+=	-lkrb -lcrypto -lcom_err
 DISTRIBUTION=	krb4
+.endif
+
+.if exists(${DESTDIR}${LIBDIR}/libkrb5.a) && defined(MAKE_KERBEROS5)
+CFLAGS+=-DKERBEROS5
+DPADD+=	${LIBKRB5} ${LIBASN1} ${LIBCRYPTO} ${LIBCRYPT} ${LIBCOM_ERR} \
+	${LIBROKEN}
+LDADD+=	-lkrb5 -lasn1 -lcrypto -lcrypt -lcom_err \
+	-L${.OBJDIR}/../../../../kerberos5/lib/libroken -lroken
+DISTRIBUTION=	krb5
 .endif
 
 BINMODE=4555
Index: su.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/su/su.c,v
retrieving revision 1.34.2.3
diff -u -r1.34.2.3 su.c
--- su.c	2001/08/17 15:44:42	1.34.2.3
+++ su.c	2002/05/16 20:42:46
@@ -69,28 +69,42 @@
 #include <skey.h>
 #endif
 
-#ifdef KERBEROS
+#ifdef KERBEROS5
+#include <com_err.h>
+#include <krb5.h>
+
+static long get_target_principal(krb5_context context, const char *user,
+    const char *ruser, char **su, krb5_principal *suprinc);
+static long kerberos5(krb5_context context, krb5_principal suprinc,
+    const char *pass);
+
+int use_kerberos5 = 1;
+#endif
+
+#ifdef KERBEROS4
 #include <openssl/des.h>
 #include <krb.h>
 #include <netdb.h>
 
-#ifdef LOGIN_CAP
-#define	ARGSTR	"-Kflmc:"
-#else
-#define	ARGSTR	"-Kflm"
-#endif
 
-static int kerberos(char *username, char *user, int uid, char *pword);
+static int kerberos4(char *username, char *user, int uid, char *pword);
 static int koktologin(char *name, char *toname);
+
+int use_kerberos4 = 1;
+#endif /* KERBEROS4 */
 
-int use_kerberos = 1;
-#else /* !KERBEROS */
 #ifdef LOGIN_CAP
-#define	ARGSTR	"-flmc:"
+#define LOGIN_CAP_ARG(x) x
 #else
-#define	ARGSTR	"-flm"
+#define LOGIN_CAP_ARG(x)
 #endif
-#endif /* KERBEROS */
+#if defined(KERBEROS4) || defined(KERBEROS5)
+#define KERBEROS_ARG(x) x
+#else
+#define KERBEROS_ARG(x)
+#endif
+#define COMMON_ARG(x) x
+#define ARGSTR	"-" COMMON_ARG("flm") LOGIN_CAP_ARG("c:") KERBEROS_ARG("K")
 
 char   *ontty __P((void));
 int	chshell __P((char *));
@@ -118,9 +132,14 @@
 	char *class=NULL;
 	int setwhat;
 #endif
-#ifdef KERBEROS
+#if defined(KERBEROS4) || defined(KERBEROS5)
 	char *k;
 #endif
+#ifdef KERBEROS5
+	char *suname, *ccname;
+	krb5_context context;
+	krb5_principal suprinc;
+#endif
 	char shellbuf[MAXPATHLEN];
 
 #ifdef WHEELSU
@@ -130,9 +149,14 @@
 	user = "root";
 	while((ch = getopt(argc, argv, ARGSTR)) != -1) 
 		switch((char)ch) {
-#ifdef KERBEROS
+#if defined(KERBEROS4) || defined(KERBEROS5)
 		case 'K':
-			use_kerberos = 0;
+#ifdef KERBEROS4
+			use_kerberos4 = 0;
+#endif
+#ifdef KERBEROS5
+			use_kerberos5 = 0;
+#endif
 			break;
 #endif
 		case 'f':
@@ -179,10 +203,22 @@
 
 	argv += optind;
 
-#ifdef KERBEROS
+#if defined(KERBEROS4) || defined(KERBEROS5)
 	k = auth_getval("auth_list");
-	if (k && !strstr(k, "kerberos"))
-	    use_kerberos = 0;
+	if (k && !strstr(k, "kerberos")) {
+#ifdef KERBEROS4
+	    use_kerberos4 = 0;
+#endif
+#ifdef KERBEROS5
+	    use_kerberos5 = 0;
+#endif
+	}
+#endif
+#ifdef KERBEROS5
+	suname = NULL;
+	suprinc = NULL;
+	if (krb5_init_context(&context) != 0)
+		use_kerberos5 = 0;
 #endif
 	errno = 0;
 	prio = getpriority(PRIO_PROCESS, 0);
@@ -235,13 +271,23 @@
 #endif /* WHEELSU */
 
 	if (ruid) {
-#ifdef KERBEROS
-		if (use_kerberos && koktologin(username, user)
+#ifdef KERBEROS4
+		if (use_kerberos4 && koktologin(username, user)
 		    && !pwd->pw_uid) {
-			warnx("kerberos: not in %s's ACL.", user);
-			use_kerberos = 0;
+			warnx("kerberos4: not in %s's ACL.", user);
+			use_kerberos4 = 0;
 		}
 #endif
+#ifdef KERBEROS5
+		if (use_kerberos5) {
+			if (!(get_target_principal(context, user, username,
+			    &suname, &suprinc) == 0 &&
+			    krb5_kuserok(context, suprinc, user))) {
+				warnx("kerberos5: not in %s's ACL.", user);
+				use_kerberos5 = 0;
+			}
+		}
+#endif
 		{
 			/*
 			 * Only allow those with pw_gid==0 or those listed in
@@ -288,15 +334,23 @@
 			p = getpass("Password:");
 			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
 #endif
-#ifdef KERBEROS
-	    			if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
-#endif
-					{
-					fprintf(stderr, "Sorry\n");
-					syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
-					exit(1);
-				}
+#ifdef KERBEROS4
+				if (use_kerberos4 && kerberos4(username, user,
+				    pwd->pw_uid, p) == 0)
+					goto authok;
+#endif
+#ifdef KERBEROS5
+				if (use_kerberos5 && kerberos5(context,
+				    suprinc, p) == 0)
+					goto authok;
+#endif
+				fprintf(stderr, "Sorry\n");
+				syslog(LOG_AUTH|LOG_WARNING,
+				    "BAD SU %s to %s%s", username, user,
+				    ontty());
+				exit(1);
 			}
+		authok:
 #ifdef WHEELSU
 			if (iswheelsu) {
 				pwd = getpwnam(user);
@@ -361,9 +415,12 @@
 	if (!asme) {
 		if (asthem) {
 			p = getenv("TERM");
-#ifdef KERBEROS
+#ifdef KERBEROS4
 			k = getenv("KRBTKFILE");
 #endif
+#ifdef KERBEROS5
+			ccname = getenv("KRB5CCNAME");
+#endif
 			if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
 				errx(1, "calloc");
 			cleanenv[0] = NULL;
@@ -376,10 +433,14 @@
 #endif
 			if (p)
 				(void)setenv("TERM", p, 1);
-#ifdef KERBEROS
+#ifdef KERBEROS4
 			if (k)
 				(void)setenv("KRBTKFILE", k, 1);
 #endif
+#ifdef KERBEROS5
+			if (ccname)
+				(void)setenv("KRB5CCNAME", ccname, 1);
+#endif
 			if (chdir(pwd->pw_dir) < 0)
 				errx(1, "no directory");
 		}
@@ -413,12 +474,8 @@
 static void
 usage()
 {
-	(void)fprintf(stderr, "usage: su [-] %s%s[login [args]]\n",
-#ifdef KERBEROS
-	    "[-Kflm] ",
-#else
-	    "[-flm] ",
-#endif
+	(void)fprintf(stderr, "usage: su [-] [-%s] %s[login [args]]\n",
+	    KERBEROS_ARG("K") COMMON_ARG("flm"),
 #ifdef LOGIN_CAP
 	    "[-c class] "
 #else
@@ -454,10 +511,146 @@
 		snprintf(buf, sizeof(buf), " on %s", p);
 	return (buf);
 }
+
+#ifdef KERBEROS5
+const char superuser[] = "root";
+
+/* Authenticate using Kerberos 5.
+ *   context -- An initialized krb5_context.
+ *   suprinc -- The target krb5_principal.
+ *   pass    -- The user's password.
+ * Note that a valid keytab in the default location with a host entry
+ * must be available.
+ * Returns 0 if authentication was successful, or a com_err error code if
+ * it was not.
+ */
+static long
+kerberos5(krb5_context context, krb5_principal suprinc, const char *pass)
+{
+	krb5_creds	 creds;
+	krb5_get_init_creds_opt gic_opt;
+	krb5_verify_init_creds_opt vic_opt;
+	long		 rv;
+
+	krb5_get_init_creds_opt_init(&gic_opt);
+	krb5_verify_init_creds_opt_init(&vic_opt);
+	rv = krb5_get_init_creds_password(context, &creds, suprinc,
+	    pass, NULL, NULL, 0, NULL, &gic_opt);
+	if (rv != 0) {
+		syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s",
+		    error_message(rv)); /* XXX */
+		return (rv);
+	}
+	krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt, 1);
+	rv = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL,
+	    &vic_opt);
+	krb5_free_cred_contents(context, &creds);
+	if (rv != 0) {
+		syslog(LOG_NOTICE|LOG_AUTH, "BAD Kerberos5 SU: %s",
+		    error_message(rv)); /* XXX */
+		return (rv);
+	}
+	return (0);
+}
+
+/* Determine the target principal given the current user and the target user.
+ *   context -- An initialized krb5_context.
+ *   user    -- The target username.
+ *   ruser   -- The current username.
+ *   su      -- (out) The target principal name.
+ *   suprinc -- (out) The target krb5_principal.
+ * When the target user is `root', the target principal will be a `root
+ * instance', e.g. `luser/root@REA.LM'.  Otherwise, the target principal
+ * will simply be the current user's default principal name.  Note that
+ * in any case, if KRB5CCNAME is set and a credentials cache exists, the
+ * principal name found there will be the `starting point', rather than
+ * the ruser parameter.
+ *
+ * Returns 0 for success, or a com_err error code on failure.
+ */
+static long
+get_target_principal(krb5_context context, const char *user, const char *ruser,
+    char **su, krb5_principal *suprinc)
+{
+	krb5_principal	 princ;
+	krb5_ccache	 ccache;
+	char		*unparsed, *p;
+	long		 rv;
+	uid_t		 euid;
+
+	*suprinc = NULL;
+	princ = NULL;
+	/* Unless KRB5CCNAME was explicitly set, we won't really be able
+	 * to look at the credentials cache since krb5_cc_default will
+	 * look at getuid().
+	 */
+	if (getenv("KRB5CCNAME") != NULL) {
+		/* Lower privs while messing about with the credentials
+		 * cache.
+		 */
+		euid = geteuid();
+		rv = seteuid(getuid());
+		if (rv != 0)
+			return (errno);
+		rv = krb5_cc_default(context, &ccache);
+		if (rv == 0) {
+			rv = krb5_cc_get_principal(context, ccache, &princ);
+			krb5_cc_close(context, ccache);
+			if (rv != 0)
+				princ = NULL; /* just to be safe */
+		}
+		rv = seteuid(euid);
+		if (rv != 0)
+			return (errno);
+	}
+	if (princ == NULL) {
+		rv = krb5_make_principal(context, &princ, NULL, ruser, NULL);
+		if (rv != 0) {
+			warnx("Could not determine default principal name.");
+			return (rv);
+		}
+	}
+	/* Now that we have some principal, if the target account is
+	 * `root', then transform it into a `root' instance, e.g.
+	 * `user@REA.LM' -> `user/root@REA.LM'.
+	 */
+	rv = krb5_unparse_name(context, princ, &unparsed);
+	krb5_free_principal(context, princ);
+	if (rv != 0) {
+		warnx("krb5_unparse_name: %s", error_message(rv));
+		return (rv);
+	}
+	if (strcmp(user, superuser) == 0) {
+		p = strrchr(unparsed, '@');
+		if (p == NULL) {
+			warnx("malformed principal name `%s'", unparsed);
+			free(unparsed);
+			return (rv);
+		}
+		*p++ = '\0';
+		*su = NULL;
+		(void)asprintf(su, "%s/%s@%s", unparsed, superuser, p);
+		free(unparsed);
+	} else 
+		*su = unparsed;
+
+	if (*su == NULL)
+		return errno;
+	rv = krb5_parse_name(context, *su, &princ);
+	if (rv != 0) {
+		warnx("krb5_parse_name `%s': %s", *su, error_message(rv));
+		free(*su);
+		return (rv);
+	}
+	*suprinc = princ;
+	return 0;
+}
+
+#endif
 
-#ifdef KERBEROS
+#ifdef KERBEROS4
 int
-kerberos(username, user, uid, pword)
+kerberos4(username, user, uid, pword)
 	char *username, *user;
 	int uid;
 	char *pword;
@@ -503,14 +696,14 @@
 
 	if (kerno != KSUCCESS) {
 		if (kerno == KDC_PR_UNKNOWN) {
-			warnx("kerberos: principal unknown: %s.%s@%s",
+			warnx("kerberos4: principal unknown: %s.%s@%s",
 				(uid == 0 ? username : user),
 				(uid == 0 ? "root" : ""), lrealm);
 			return (1);
 		}
-		warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
+		warnx("kerberos4: unable to su: %s", krb_err_txt[kerno]);
 		syslog(LOG_NOTICE|LOG_AUTH,
-		    "BAD Kerberos SU: %s to %s%s: %s",
+		    "BAD Kerberos4 SU: %s to %s%s: %s",
 		    username, user, ontty(), krb_err_txt[kerno]);
 		return (1);
 	}
@@ -556,7 +749,7 @@
 
 		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
 		    &authdata, "")) != KSUCCESS) {
-			warnx("kerberos: unable to verify rcmd ticket: %s\n",
+			warnx("kerberos4: unable to verify rcmd ticket: %s\n",
 			    krb_err_txt[kerno]);
 			syslog(LOG_NOTICE|LOG_AUTH,
 			    "failed su: %s to %s%s: %s", username,

--dDRMvlgZJXvWKvBx
Content-Type: application/x-shar
Content-Disposition: attachment; filename="pam_ksu.shar"
Content-Transfer-Encoding: quoted-printable

# This is a shell archive.  Save it in a file, remove anything before=0A# t=
his line, and then unpack it by entering "sh file".  Note, it may=0A# creat=
e directories; files and directories will be owned by you and=0A# have defa=
ult permissions.=0A#=0A# This archive contains:=0A#=0A#	pam_ksu=0A#	pam_ksu=
/Makefile=0A#	pam_ksu/pam_ksu.8=0A#	pam_ksu/pam_ksu.c=0A#=0Aecho c - pam_ks=
u=0Amkdir -p pam_ksu > /dev/null 2>&1=0Aecho x - pam_ksu/Makefile=0Ased 's/=
^X//' >pam_ksu/Makefile << 'END-of-pam_ksu/Makefile'=0AX# Copyright 2002 Fr=
eeBSD, Inc.=0AX# All rights reserved.=0AX#=0AX# Redistribution and use in s=
ource and binary forms, with or without=0AX# modification, are permitted pr=
ovided that the following conditions=0AX# are met:=0AX# 1. Redistributions =
of source code must retain the above copyright=0AX#    notice, this list of=
 conditions and the following disclaimer.=0AX# 2. Redistributions in binary=
 form must reproduce the above copyright=0AX#    notice, this list of condi=
tions and the following disclaimer in the=0AX#    documentation and/or othe=
r materials provided with the distribution.=0AX#=0AX# THIS SOFTWARE IS PROV=
IDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND=0AX# ANY EXPRESS OR IMPLI=
ED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0AX# IMPLIED WARRANTIES O=
F MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE=0AX# ARE DISCLAIMED.=
  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE=0AX# FOR ANY DIREC=
T, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL=0AX# DAMAGES =
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS=0AX# OR SER=
VICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)=0AX# HOWEVE=
R CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT=0AX# L=
IABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY=0A=
X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=
=0AX# SUCH DAMAGE.=0AX#=0AX#	$FreeBSD: src/lib/libpam/modules/pam_krb5/Make=
file,v 1.2.2.3 2001/07/30 10:03:58 markm Exp $=0AX=0AXPAMDIR=3D		${.CURDIR}=
/../../../../contrib/libpam=0AX=0AXLIB=3D		pam_ksu=0AXSRCS=3D		pam_ksu.c=0A=
XCFLAGS+=3D	-Wall=0AXCFLAGS+=3D	-I${PAMDIR}/libpam/include=0AXCFLAGS+=3D	-I=
${.CURDIR}/../../libpam=0AXDPADD=3D		${LIBKRB5} ${LIBASN1} ${LIBCRYPTO} ${L=
IBCRYPT} \=0AX		${LIBCOM_ERR} ${LIBROKEN}=0AXLDADD=3D		-lkrb5 -lasn1 -lcryp=
to -lcrypt -lcom_err \=0AX		-L${.OBJDIR}/../../../../kerberos5/lib/libroken=
 -lroken =0AXMAN=3D		pam_ksu.8=0AX#INTERNALLIB=3D	yes=0AX#INTERNALSTATICLIB=
=3Dyes=0AX=0AX.include <bsd.lib.mk>=0AEND-of-pam_ksu/Makefile=0Aecho x - pa=
m_ksu/pam_ksu.8=0Ased 's/^X//' >pam_ksu/pam_ksu.8 << 'END-of-pam_ksu/pam_ks=
u.8'=0AX.\" Copyright (c) 2001 Mark R V Murray=0AX.\" All rights reserved.=
=0AX.\" Copyright (c) 2001 Networks Associates Technology, Inc.=0AX.\" All =
rights reserved.=0AX.\"=0AX.\" This software was developed for the FreeBSD =
Project by ThinkSec AS and=0AX.\" NAI Labs, the Security Research Division =
of Network Associates, Inc.=0AX.\" under DARPA/SPAWAR contract N66001-01-C-=
8035 ("CBOSS"), as part of the=0AX.\" DARPA CHATS research program.=0AX.\"=
=0AX.\" Redistribution and use in source and binary forms, with or without=
=0AX.\" modification, are permitted provided that the following conditions=
=0AX.\" are met:=0AX.\" 1. Redistributions of source code must retain the a=
bove copyright=0AX.\"    notice, this list of conditions and the following =
disclaimer.=0AX.\" 2. Redistributions in binary form must reproduce the abo=
ve copyright=0AX.\"    notice, this list of conditions and the following di=
sclaimer in the=0AX.\"    documentation and/or other materials provided wit=
h the distribution.=0AX.\" 3. The name of the author may not be used to end=
orse or promote=0AX.\"    products derived from this software without speci=
fic prior written=0AX.\"    permission.=0AX.\"=0AX.\" THIS SOFTWARE IS PROV=
IDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND=0AX.\" ANY EXPRESS OR IMP=
LIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0AX.\" IMPLIED WARRANTI=
ES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE=0AX.\" ARE DISCL=
AIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE=0AX.\" FOR A=
NY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL=0AX.\=
" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS=
=0AX.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTIO=
N)=0AX.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRAC=
T, STRICT=0AX.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARI=
SING IN ANY WAY=0AX.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF =
THE POSSIBILITY OF=0AX.\" SUCH DAMAGE.=0AX.\"=0AX.\" $FreeBSD$=0AX.\"=0AX.D=
d May 15, 2002=0AX.Dt pam_ksu 8=0AX.Os=0AX.Sh NAME=0AX.Nm pam_ksu=0AX.Nd Ke=
rberos 5 SU PAM module=0AX.Sh SYNOPSIS=0AX.Op Ar service-name=0AX.Ar module=
-type=0AX.Ar control-flag=0AX.Pa pam_ksu=0AX.Op Ar options=0AX.Sh DESCRIPTI=
ON=0AXThe Kerberos 5 SU authentication service module for PAM,=0AX.Nm=0AXfo=
r only one PAM category: authentication.=0AXIn terms of the=0AX.Ar module-t=
ype=0AXparameter, this is the=0AX.Dq Li auth=0AXfeature.=0AXThe module is s=
pecifically designed to be used with the=0AX.Xr su 1=0AXutility.=0AX.\" It =
also provides a null function for session management.=0AX.Ss Kerberos 5 SU =
Authentication Module=0AXThe Kerberos 5 SU authentication component provide=
s functions to verify=0AXthe identity of a user=0AX.Pq Fn pam_sm_authentica=
te ,=0AXand determine whether or not the user is authorized to obtain the=
=0AXprivileges of the target account.=0AXIf the target account is `root', t=
hen the Kerberos 5 principal used=0AXfor authentication and authorization w=
ill be the `root' instance of=0AXthe current user, e.g. `user/root@REAL.M'.=
=0AXOtherwise, the principal will simply be the current user's default=0AXp=
rincipal, e.g. `user@REAL.M'.=0AX.Pp=0AXThe user is prompted for a password=
 if necessary.  Authorization is performed=0AXby comparing the Kerberos 5 p=
rincipal with those listed in the=0AX.Pa .k5login=0AXfile in the target acc=
ount's home directory=0AX.Pq e.g. /root/.k5login for root .=0AX.Pp=0AXThe f=
ollowing options may be passed to the authentication module:=0AX.Bl -tag -w=
idth ".Cm use_first_pass"=0AX.It Cm debug=0AX.Xr syslog 3=0AXdebugging info=
rmation at=0AX.Dv LOG_DEBUG=0AXlevel.=0AX.It Cm use_first_pass=0AXIf the au=
thentication module=0AXis not the first in the stack,=0AXand a previous mod=
ule=0AXobtained the user's password,=0AXthat password is used=0AXto authent=
icate the user.=0AXIf this fails,=0AXthe authentication module returns fail=
ure=0AXwithout prompting the user for a password.=0AXThis option has no eff=
ect=0AXif the authentication module=0AXis the first in the stack,=0AXor if =
no previous modules=0AXobtained the user's password.=0AX.It Cm try_first_pa=
ss=0AXThis option is similar to the=0AX.Cm use_first_pass=0AXoption,=0AXexc=
ept that if the previously obtained password fails,=0AXthe user is prompted=
 for another password.=0AX.El=0AX.Sh SEE ALSO=0AX.Xr su 1 ,=0AX.Xr syslog 3=
 ,=0AX.Xr pam.conf 5 ,=0AX.Xr pam 8=0AEND-of-pam_ksu/pam_ksu.8=0Aecho x - p=
am_ksu/pam_ksu.c=0Ased 's/^X//' >pam_ksu/pam_ksu.c << 'END-of-pam_ksu/pam_k=
su.c'=0AX/*-=0AX * Copyright (c) 2002 Jacques A. Vidrine <nectar@FreeBSD.or=
g>=0AX * All rights reserved.=0AX *=0AX * Redistribution and use in source =
and binary forms, with or without=0AX * modification, are permitted provide=
d that the following conditions=0AX * are met:=0AX * 1. Redistributions of =
source code must retain the above copyright=0AX *    notice, this list of c=
onditions and the following disclaimer.=0AX * 2. Redistributions in binary =
form must reproduce the above copyright=0AX *    notice, this list of condi=
tions and the following disclaimer in the=0AX *    documentation and/or oth=
er materials provided with the distribution.=0AX *=0AX * THIS SOFTWARE IS P=
ROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND=0AX * ANY EXPRESS OR I=
MPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0AX * IMPLIED WARRANT=
IES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE=0AX * ARE DISCL=
AIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE=0AX * FOR AN=
Y DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL=0AX * =
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS=0AX=
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)=0A=
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STR=
ICT=0AX * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN=
 ANY WAY=0AX * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSS=
IBILITY OF=0AX * SUCH DAMAGE.=0AX */=0AX#include <sys/cdefs.h>=0AX__FBSDID(=
"$FreeBSD$");=0AX=0AX#include <sys/param.h>=0AX#include <com_err.h>=0AX#inc=
lude <errno.h>=0AX#include <stdio.h>=0AX#include <stdlib.h>=0AX#include <st=
ring.h>=0AX#include <unistd.h>=0AX=0AX#include <krb5.h>=0AX=0AX#define PAM_=
SM_AUTH=0AX#define PAM_SM_CRED=0AX#include <security/pam_appl.h>=0AX#includ=
e <security/pam_modules.h>=0AX#include <security/pam_mod_misc.h>=0AX=0AXsta=
tic const char superuser[] =3D "root";=0AX=0AXstatic long	get_target_princi=
pal(krb5_context, const char *, const char *,=0AX		    char **su, krb5_prin=
cipal *);=0AXstatic int	auth_krb5(pam_handle_t *, krb5_context, const char =
*,=0AX		    krb5_principal);=0AX=0AXstatic struct opttab other_options[] =
=3D {=0AX	{ NULL, 0 }=0AX};=0AX=0AX=0AXPAM_EXTERN int=0AXpam_sm_authenticat=
e(pam_handle_t *pamh, int flags __unused, int ac __unused,=0AX    const cha=
r *av[] __unused)=0AX{=0AX	struct options	 options;=0AX	krb5_context	 conte=
xt;=0AX	krb5_principal	 suprinc;=0AX	const char	*user, *ruser;=0AX	char		*s=
u;=0AX	long		 rv;=0AX	int		 pamret;=0AX	=0AX	pam_std_option(&options, other=
_options, ac, av);=0AX	PAM_LOG("pam_ksu: Options processed");=0AX	pamret =
=3D pam_get_user(pamh, &user, NULL);=0AX	if (pamret !=3D PAM_SUCCESS)=0AX		=
return (pamret);=0AX	PAM_LOG("Got user: %s", user);=0AX	pamret =3D pam_get_=
item(pamh, PAM_RUSER, (const void **)&ruser);=0AX	if (pamret !=3D PAM_SUCCE=
SS)=0AX		return (pamret);=0AX	PAM_LOG("Got ruser: %s", ruser);=0AX	rv =3D k=
rb5_init_context(&context);=0AX	if (rv !=3D 0) {=0AX		PAM_LOG("krb5_init_co=
ntext failed: %s", error_message(rv));=0AX		return (PAM_SERVICE_ERR);=0AX	}=
=0AX	rv =3D get_target_principal(context, user, ruser, &su, &suprinc);=0AX	=
if (rv !=3D 0)=0AX		return (PAM_AUTH_ERR);=0AX	PAM_LOG("kuserok: %s -> %s",=
 su, user);=0AX	rv =3D krb5_kuserok(context, suprinc, user);=0AX	pamret =3D=
 rv ? auth_krb5(pamh, context, su, suprinc) : PAM_AUTH_ERR;=0AX	free(su);=
=0AX	krb5_free_principal(context, suprinc);=0AX	krb5_free_context(context);=
=0AX	return (pamret);=0AX}=0AX=0AXPAM_EXTERN int=0AXpam_sm_setcred(pam_hand=
le_t *pamh __unused, int flags __unused,=0AX    int ac __unused, const char=
 *av[] __unused)=0AX{=0AX	return (PAM_SUCCESS);=0AX}=0AX=0AX/* Authenticate=
 using Kerberos 5.=0AX *   pamh    -- The PAM handle.=0AX *   context -- An=
 initialized krb5_context.=0AX *   su      -- The target principal name, us=
ed only for password prompts.=0AX *              If NULL, the password prom=
pts will not include a principal=0AX *              name.=0AX *   suprinc -=
- The target krb5_principal.=0AX * Note that a valid keytab in the default =
location with a host entry=0AX * must be available, and that the PAM applic=
ation must have sufficient=0AX * privileges to access it.=0AX * Returns PAM=
_SUCCESS if authentication was successful, or an appropriate=0AX * PAM erro=
r code if it was not.=0AX */=0AXstatic int=0AXauth_krb5(pam_handle_t *pamh,=
 krb5_context context, const char *su,=0AX    krb5_principal suprinc)=0AX{=
=0AX	krb5_creds	 creds;=0AX	krb5_get_init_creds_opt gic_opt;=0AX	krb5_verif=
y_init_creds_opt vic_opt;=0AX	const char	*pass;=0AX	char		*prompt;=0AX	long=
		 rv;=0AX	int		 pamret;=0AX=0AX	prompt =3D NULL;=0AX	krb5_get_init_creds_o=
pt_init(&gic_opt);=0AX	krb5_verify_init_creds_opt_init(&vic_opt);=0AX	if (s=
u !=3D NULL)=0AX		(void)asprintf(&prompt, "Password for %s:", su);=0AX	else=
=0AX		(void)asprintf(&prompt, "Password:");=0AX	if (prompt =3D=3D NULL)=0AX=
		return (PAM_BUF_ERR);=0AX	pass =3D NULL;=0AX	(void)pam_get_item(pamh, PAM=
_AUTHTOK, (const void **)&pass);=0AX	free(prompt);=0AX	if (pass =3D=3D NULL=
) {=0AX		pamret =3D pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);=0AX	=
	if (pamret !=3D PAM_SUCCESS)=0AX			return (pamret);=0AX	}=0AX	rv =3D krb5_=
get_init_creds_password(context, &creds, suprinc,=0AX	    pass, NULL, NULL,=
 0, NULL, &gic_opt);=0AX	if (rv !=3D 0) {=0AX		PAM_LOG("krb5_get_init_creds=
_password: %s", error_message(rv));=0AX		return (PAM_AUTH_ERR);=0AX	}=0AX	k=
rb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt, 1);=0AX	rv =3D krb5_v=
erify_init_creds(context, &creds, NULL, NULL, NULL,=0AX	    &vic_opt);=0AX	=
krb5_free_cred_contents(context, &creds);=0AX	if (rv !=3D 0) {=0AX		PAM_LOG=
("krb5_verify_init_creds: %s", error_message(rv));=0AX		return (PAM_AUTH_ER=
R);=0AX	}=0AX	return (PAM_SUCCESS);=0AX}=0AX=0AX/* Determine the target pri=
ncipal given the current user and the target user.=0AX *   context -- An in=
itialized krb5_context.=0AX *   user    -- The target username.=0AX *   rus=
er   -- The current username.=0AX *   su      -- (out) The target principal=
 name.=0AX *   suprinc -- (out) The target krb5_principal.=0AX * When the t=
arget user is `root', the target principal will be a `root=0AX * instance',=
 e.g. `luser/root@REA.LM'.  Otherwise, the target principal=0AX * will simp=
ly be the current user's default principal name.  Note that=0AX * in any ca=
se, if KRB5CCNAME is set and a credentials cache exists, the=0AX * principa=
l name found there will be the `starting point', rather than=0AX * the ruse=
r parameter.=0AX *=0AX * Returns 0 for success, or a com_err error code on =
failure.=0AX */=0AXstatic long=0AXget_target_principal(krb5_context context=
, const char *user, const char *ruser,=0AX    char **su, krb5_principal *su=
princ)=0AX{=0AX	krb5_principal	 princ;=0AX	krb5_ccache	 ccache;=0AX	char		*=
unparsed, *p;=0AX	long		 rv;=0AX	uid_t		 euid;=0AX=0AX	*suprinc =3D NULL;=
=0AX	princ =3D NULL;=0AX	/* Unless KRB5CCNAME was explicitly set, we won't =
really be able=0AX	 * to look at the credentials cache since krb5_cc_defaul=
t will=0AX	 * look at getuid().=0AX	 */=0AX	if (getenv("KRB5CCNAME") !=3D N=
ULL) {=0AX		/* Lower privs while messing about with the credentials=0AX		 *=
 cache.=0AX		 */=0AX		euid =3D geteuid();=0AX		rv =3D seteuid(getuid());=0A=
X		if (rv !=3D 0)=0AX			return (errno);=0AX		rv =3D krb5_cc_default(context=
, &ccache);=0AX		if (rv =3D=3D 0) {=0AX			rv =3D krb5_cc_get_principal(cont=
ext, ccache, &princ);=0AX			krb5_cc_close(context, ccache);=0AX			if (rv !=
=3D 0)=0AX				princ =3D NULL; /* just to be safe */=0AX		}=0AX		rv =3D sete=
uid(euid);=0AX		if (rv !=3D 0)=0AX			return (errno);=0AX	}=0AX	if (princ =
=3D=3D NULL) {=0AX		rv =3D krb5_make_principal(context, &princ, NULL, ruser=
, NULL);=0AX		if (rv !=3D 0) {=0AX			PAM_LOG("Could not determine default p=
rincipal name.");=0AX			return (rv);=0AX		}=0AX	}=0AX	/* Now that we have s=
ome principal, if the target account is=0AX	 * `root', then transform it in=
to a `root' instance, e.g.=0AX	 * `user@REA.LM' -> `user/root@REA.LM'.=0AX	=
 */=0AX	rv =3D krb5_unparse_name(context, princ, &unparsed);=0AX	krb5_free_=
principal(context, princ);=0AX	if (rv !=3D 0) {=0AX		PAM_LOG("krb5_unparse_=
name: %s", error_message(rv));=0AX		return (rv);=0AX	}=0AX	PAM_LOG("Default=
 principal name: %s", unparsed);=0AX	if (strcmp(user, superuser) =3D=3D 0) =
{=0AX		p =3D strrchr(unparsed, '@');=0AX		if (p =3D=3D NULL) {=0AX			PAM_LO=
G("malformed principal name `%s'", unparsed);=0AX			free(unparsed);=0AX			r=
eturn (rv);=0AX		}=0AX		*p++ =3D '\0';=0AX		*su =3D NULL;=0AX		(void)asprin=
tf(su, "%s/%s@%s", unparsed, superuser, p);=0AX		free(unparsed);=0AX	} else=
 =0AX		*su =3D unparsed;=0AX=0AX	if (*su =3D=3D NULL)=0AX		return errno;=0A=
X	rv =3D krb5_parse_name(context, *su, &princ);=0AX	if (rv !=3D 0) {=0AX		P=
AM_LOG("krb5_parse_name `%s': %s", *su, error_message(rv));=0AX		free(*su);=
=0AX		return (rv);=0AX	}=0AX	PAM_LOG("Target principal name: %s", *su);=0AX=
	*suprinc =3D princ;=0AX	return 0;=0AX}=0AEND-of-pam_ksu/pam_ksu.c=0Aexit=
=0A=0A
--dDRMvlgZJXvWKvBx--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




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