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
[-- Attachment #1 --]
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.
[-- Attachment #2 --]
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,
[-- Attachment #3 --]
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# pam_ksu
# pam_ksu/Makefile
# pam_ksu/pam_ksu.8
# pam_ksu/pam_ksu.c
#
echo c - pam_ksu
mkdir -p pam_ksu > /dev/null 2>&1
echo x - pam_ksu/Makefile
sed 's/^X//' >pam_ksu/Makefile << 'END-of-pam_ksu/Makefile'
X# Copyright 2002 FreeBSD, Inc.
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms, with or without
X# modification, are permitted provided that the following conditions
X# are met:
X# 1. Redistributions of source code must retain the above copyright
X# notice, this list of conditions and the following disclaimer.
X# 2. Redistributions in binary form must reproduce the above copyright
X# notice, this list of conditions and the following disclaimer in the
X# documentation and/or other materials provided with the distribution.
X#
X# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X# SUCH DAMAGE.
X#
X# $FreeBSD: src/lib/libpam/modules/pam_krb5/Makefile,v 1.2.2.3 2001/07/30 10:03:58 markm Exp $
X
XPAMDIR= ${.CURDIR}/../../../../contrib/libpam
X
XLIB= pam_ksu
XSRCS= pam_ksu.c
XCFLAGS+= -Wall
XCFLAGS+= -I${PAMDIR}/libpam/include
XCFLAGS+= -I${.CURDIR}/../../libpam
XDPADD= ${LIBKRB5} ${LIBASN1} ${LIBCRYPTO} ${LIBCRYPT} \
X ${LIBCOM_ERR} ${LIBROKEN}
XLDADD= -lkrb5 -lasn1 -lcrypto -lcrypt -lcom_err \
X -L${.OBJDIR}/../../../../kerberos5/lib/libroken -lroken
XMAN= pam_ksu.8
X#INTERNALLIB= yes
X#INTERNALSTATICLIB=yes
X
X.include <bsd.lib.mk>
END-of-pam_ksu/Makefile
echo x - pam_ksu/pam_ksu.8
sed 's/^X//' >pam_ksu/pam_ksu.8 << 'END-of-pam_ksu/pam_ksu.8'
X.\" Copyright (c) 2001 Mark R V Murray
X.\" All rights reserved.
X.\" Copyright (c) 2001 Networks Associates Technology, Inc.
X.\" All rights reserved.
X.\"
X.\" This software was developed for the FreeBSD Project by ThinkSec AS and
X.\" NAI Labs, the Security Research Division of Network Associates, Inc.
X.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
X.\" DARPA CHATS research program.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\" notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\" notice, this list of conditions and the following disclaimer in the
X.\" documentation and/or other materials provided with the distribution.
X.\" 3. The name of the author may not be used to endorse or promote
X.\" products derived from this software without specific prior written
X.\" permission.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\" $FreeBSD$
X.\"
X.Dd May 15, 2002
X.Dt pam_ksu 8
X.Os
X.Sh NAME
X.Nm pam_ksu
X.Nd Kerberos 5 SU PAM module
X.Sh SYNOPSIS
X.Op Ar service-name
X.Ar module-type
X.Ar control-flag
X.Pa pam_ksu
X.Op Ar options
X.Sh DESCRIPTION
XThe Kerberos 5 SU authentication service module for PAM,
X.Nm
Xfor only one PAM category: authentication.
XIn terms of the
X.Ar module-type
Xparameter, this is the
X.Dq Li auth
Xfeature.
XThe module is specifically designed to be used with the
X.Xr su 1
Xutility.
X.\" It also provides a null function for session management.
X.Ss Kerberos 5 SU Authentication Module
XThe Kerberos 5 SU authentication component provides functions to verify
Xthe identity of a user
X.Pq Fn pam_sm_authenticate ,
Xand determine whether or not the user is authorized to obtain the
Xprivileges of the target account.
XIf the target account is `root', then the Kerberos 5 principal used
Xfor authentication and authorization will be the `root' instance of
Xthe current user, e.g. `user/root@REAL.M'.
XOtherwise, the principal will simply be the current user's default
Xprincipal, e.g. `user@REAL.M'.
X.Pp
XThe user is prompted for a password if necessary. Authorization is performed
Xby comparing the Kerberos 5 principal with those listed in the
X.Pa .k5login
Xfile in the target account's home directory
X.Pq e.g. /root/.k5login for root .
X.Pp
XThe following options may be passed to the authentication module:
X.Bl -tag -width ".Cm use_first_pass"
X.It Cm debug
X.Xr syslog 3
Xdebugging information at
X.Dv LOG_DEBUG
Xlevel.
X.It Cm use_first_pass
XIf the authentication module
Xis not the first in the stack,
Xand a previous module
Xobtained the user's password,
Xthat password is used
Xto authenticate the user.
XIf this fails,
Xthe authentication module returns failure
Xwithout prompting the user for a password.
XThis option has no effect
Xif the authentication module
Xis the first in the stack,
Xor if no previous modules
Xobtained the user's password.
X.It Cm try_first_pass
XThis option is similar to the
X.Cm use_first_pass
Xoption,
Xexcept that if the previously obtained password fails,
Xthe user is prompted for another password.
X.El
X.Sh SEE ALSO
X.Xr su 1 ,
X.Xr syslog 3 ,
X.Xr pam.conf 5 ,
X.Xr pam 8
END-of-pam_ksu/pam_ksu.8
echo x - pam_ksu/pam_ksu.c
sed 's/^X//' >pam_ksu/pam_ksu.c << 'END-of-pam_ksu/pam_ksu.c'
X/*-
X * Copyright (c) 2002 Jacques A. Vidrine <nectar@FreeBSD.org>
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X#include <sys/cdefs.h>
X__FBSDID("$FreeBSD$");
X
X#include <sys/param.h>
X#include <com_err.h>
X#include <errno.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <unistd.h>
X
X#include <krb5.h>
X
X#define PAM_SM_AUTH
X#define PAM_SM_CRED
X#include <security/pam_appl.h>
X#include <security/pam_modules.h>
X#include <security/pam_mod_misc.h>
X
Xstatic const char superuser[] = "root";
X
Xstatic long get_target_principal(krb5_context, const char *, const char *,
X char **su, krb5_principal *);
Xstatic int auth_krb5(pam_handle_t *, krb5_context, const char *,
X krb5_principal);
X
Xstatic struct opttab other_options[] = {
X { NULL, 0 }
X};
X
X
XPAM_EXTERN int
Xpam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int ac __unused,
X const char *av[] __unused)
X{
X struct options options;
X krb5_context context;
X krb5_principal suprinc;
X const char *user, *ruser;
X char *su;
X long rv;
X int pamret;
X
X pam_std_option(&options, other_options, ac, av);
X PAM_LOG("pam_ksu: Options processed");
X pamret = pam_get_user(pamh, &user, NULL);
X if (pamret != PAM_SUCCESS)
X return (pamret);
X PAM_LOG("Got user: %s", user);
X pamret = pam_get_item(pamh, PAM_RUSER, (const void **)&ruser);
X if (pamret != PAM_SUCCESS)
X return (pamret);
X PAM_LOG("Got ruser: %s", ruser);
X rv = krb5_init_context(&context);
X if (rv != 0) {
X PAM_LOG("krb5_init_context failed: %s", error_message(rv));
X return (PAM_SERVICE_ERR);
X }
X rv = get_target_principal(context, user, ruser, &su, &suprinc);
X if (rv != 0)
X return (PAM_AUTH_ERR);
X PAM_LOG("kuserok: %s -> %s", su, user);
X rv = krb5_kuserok(context, suprinc, user);
X pamret = rv ? auth_krb5(pamh, context, su, suprinc) : PAM_AUTH_ERR;
X free(su);
X krb5_free_principal(context, suprinc);
X krb5_free_context(context);
X return (pamret);
X}
X
XPAM_EXTERN int
Xpam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused,
X int ac __unused, const char *av[] __unused)
X{
X return (PAM_SUCCESS);
X}
X
X/* Authenticate using Kerberos 5.
X * pamh -- The PAM handle.
X * context -- An initialized krb5_context.
X * su -- The target principal name, used only for password prompts.
X * If NULL, the password prompts will not include a principal
X * name.
X * suprinc -- The target krb5_principal.
X * Note that a valid keytab in the default location with a host entry
X * must be available, and that the PAM application must have sufficient
X * privileges to access it.
X * Returns PAM_SUCCESS if authentication was successful, or an appropriate
X * PAM error code if it was not.
X */
Xstatic int
Xauth_krb5(pam_handle_t *pamh, krb5_context context, const char *su,
X krb5_principal suprinc)
X{
X krb5_creds creds;
X krb5_get_init_creds_opt gic_opt;
X krb5_verify_init_creds_opt vic_opt;
X const char *pass;
X char *prompt;
X long rv;
X int pamret;
X
X prompt = NULL;
X krb5_get_init_creds_opt_init(&gic_opt);
X krb5_verify_init_creds_opt_init(&vic_opt);
X if (su != NULL)
X (void)asprintf(&prompt, "Password for %s:", su);
X else
X (void)asprintf(&prompt, "Password:");
X if (prompt == NULL)
X return (PAM_BUF_ERR);
X pass = NULL;
X (void)pam_get_item(pamh, PAM_AUTHTOK, (const void **)&pass);
X free(prompt);
X if (pass == NULL) {
X pamret = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt);
X if (pamret != PAM_SUCCESS)
X return (pamret);
X }
X rv = krb5_get_init_creds_password(context, &creds, suprinc,
X pass, NULL, NULL, 0, NULL, &gic_opt);
X if (rv != 0) {
X PAM_LOG("krb5_get_init_creds_password: %s", error_message(rv));
X return (PAM_AUTH_ERR);
X }
X krb5_verify_init_creds_opt_set_ap_req_nofail(&vic_opt, 1);
X rv = krb5_verify_init_creds(context, &creds, NULL, NULL, NULL,
X &vic_opt);
X krb5_free_cred_contents(context, &creds);
X if (rv != 0) {
X PAM_LOG("krb5_verify_init_creds: %s", error_message(rv));
X return (PAM_AUTH_ERR);
X }
X return (PAM_SUCCESS);
X}
X
X/* Determine the target principal given the current user and the target user.
X * context -- An initialized krb5_context.
X * user -- The target username.
X * ruser -- The current username.
X * su -- (out) The target principal name.
X * suprinc -- (out) The target krb5_principal.
X * When the target user is `root', the target principal will be a `root
X * instance', e.g. `luser/root@REA.LM'. Otherwise, the target principal
X * will simply be the current user's default principal name. Note that
X * in any case, if KRB5CCNAME is set and a credentials cache exists, the
X * principal name found there will be the `starting point', rather than
X * the ruser parameter.
X *
X * Returns 0 for success, or a com_err error code on failure.
X */
Xstatic long
Xget_target_principal(krb5_context context, const char *user, const char *ruser,
X char **su, krb5_principal *suprinc)
X{
X krb5_principal princ;
X krb5_ccache ccache;
X char *unparsed, *p;
X long rv;
X uid_t euid;
X
X *suprinc = NULL;
X princ = NULL;
X /* Unless KRB5CCNAME was explicitly set, we won't really be able
X * to look at the credentials cache since krb5_cc_default will
X * look at getuid().
X */
X if (getenv("KRB5CCNAME") != NULL) {
X /* Lower privs while messing about with the credentials
X * cache.
X */
X euid = geteuid();
X rv = seteuid(getuid());
X if (rv != 0)
X return (errno);
X rv = krb5_cc_default(context, &ccache);
X if (rv == 0) {
X rv = krb5_cc_get_principal(context, ccache, &princ);
X krb5_cc_close(context, ccache);
X if (rv != 0)
X princ = NULL; /* just to be safe */
X }
X rv = seteuid(euid);
X if (rv != 0)
X return (errno);
X }
X if (princ == NULL) {
X rv = krb5_make_principal(context, &princ, NULL, ruser, NULL);
X if (rv != 0) {
X PAM_LOG("Could not determine default principal name.");
X return (rv);
X }
X }
X /* Now that we have some principal, if the target account is
X * `root', then transform it into a `root' instance, e.g.
X * `user@REA.LM' -> `user/root@REA.LM'.
X */
X rv = krb5_unparse_name(context, princ, &unparsed);
X krb5_free_principal(context, princ);
X if (rv != 0) {
X PAM_LOG("krb5_unparse_name: %s", error_message(rv));
X return (rv);
X }
X PAM_LOG("Default principal name: %s", unparsed);
X if (strcmp(user, superuser) == 0) {
X p = strrchr(unparsed, '@');
X if (p == NULL) {
X PAM_LOG("malformed principal name `%s'", unparsed);
X free(unparsed);
X return (rv);
X }
X *p++ = '\0';
X *su = NULL;
X (void)asprintf(su, "%s/%s@%s", unparsed, superuser, p);
X free(unparsed);
X } else
X *su = unparsed;
X
X if (*su == NULL)
X return errno;
X rv = krb5_parse_name(context, *su, &princ);
X if (rv != 0) {
X PAM_LOG("krb5_parse_name `%s': %s", *su, error_message(rv));
X free(*su);
X return (rv);
X }
X PAM_LOG("Target principal name: %s", *su);
X *suprinc = princ;
X return 0;
X}
END-of-pam_ksu/pam_ksu.c
exit
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020516204525.GA26640>
