authentication but does not use the kerberos cookie scheme.
--with-kerb5
- Enable kerberos v5 support. This enables with kerberos v4 support
- and links with the standard kerberos v5 libraries as well as the v4
- compatibility libraries. This uses kerberos passphrases for
- authentication but does not use the kerberos cookie scheme.
+ Enable kerberos v5 support. Tested against MIT Kerberos V,
+ release 1.1, although also expected to work against CNS. This
+ This uses kerberos passphrases for authentication but does not
+ use the kerberos cookie scheme.
--with-authenticate
Enable support for the AIX 4.x general authentication function.
#ifdef HAVE_KERB4
# include <krb.h>
#endif /* HAVE_KERB4 */
+#ifdef HAVE_KERB5
+# include <krb5.h>
+#endif /* HAVE_KERB5 */
#ifdef HAVE_PAM
# include <security/pam_appl.h>
# include <security/pam_misc.h>
#ifdef HAVE_KERB4
static int sudo_krb_validate_user __P((struct passwd *, char *));
#endif /* HAVE_KERB4 */
+#ifdef HAVE_KERB5
+static int sudo_krb5_validate_user __P((struct passwd *, char *));
+static int verify_krb_v5_tgt __P((krb5_ccache));
+#endif /* HAVE_KERB5 */
#ifdef HAVE_PAM
static void pam_attempt_auth __P((void));
#endif /* HAVE_PAM */
#ifdef HAVE_OPIE
struct opie opie;
#endif
+#ifdef HAVE_KERB5
+extern krb5_context sudo_context;
+extern char *realm;
+extern int xrealm;
+#endif /* HAVE_KERB5 */
reenter = 1;
if (authenticate(user_name, pass, &reenter, &message) == 0)
return; /* valid password */
-#else
+#else /* HAVE_AUTHENTICATE */
# ifdef HAVE_SKEY
/* rewrite the prompt if using s/key since the challenge can change */
set_perms(PERM_ROOT, 0);
return;
# endif /* HAVE_KERB4 */
+# ifdef HAVE_KERB5
+ if (sudo_krb5_validate_user(user_pw_ent, pass) == 0)
+ return;
+# endif /* HAVE_KERB5 */
+
# ifdef HAVE_AFS
if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION,
user_name, /* name */
}
#endif /* HAVE_KERB4 */
+
+#ifdef HAVE_KERB5
+/********************************************************************
+ *
+ * sudo_krb5_validate_user()
+ *
+ * Validate a user via Kerberos 5. We may lose a bit of memory, but it's
+ * OK since we're a short lived program. I'd rather do that than contort
+ * the code to handle the cleanup.
+ */
+static int sudo_krb5_validate_user(pw, pass)
+ struct passwd *pw;
+ char *pass;
+{
+ krb5_error_code retval;
+ krb5_principal princ;
+ krb5_creds creds;
+ krb5_ccache ccache;
+ char cache_name[64];
+ char *princ_name;
+ krb5_get_init_creds_opt opts;
+
+ /* Initialize */
+ if (!sudo_context)
+ return -1;
+ krb5_get_init_creds_opt_init(&opts);
+
+ princ_name = malloc(strlen(pw->pw_name) + strlen(realm) + 2);
+ if (!princ_name) {
+ (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]);
+ exit(1);
+ }
+
+ sprintf(princ_name, "%s@%s", pw->pw_name, realm);
+ if (retval = krb5_parse_name(sudo_context, princ_name, &princ))
+ return retval;
+
+ /* Set the ticket file to be in /tmp so we don't need to change perms. */
+ (void) sprintf(cache_name, "FILE:/tmp/sudocc_%ld", getpid());
+ if (retval = krb5_cc_resolve(sudo_context, cache_name, &ccache))
+ return retval;
+
+ if (retval = krb5_get_init_creds_password(sudo_context, &creds, princ,
+ pass, krb5_prompter_posix, NULL,
+ 0, NULL, &opts))
+ return retval;
+
+ /* Stash the TGT so we can verify it. */
+ if (retval = krb5_cc_initialize(sudo_context, ccache, princ))
+ return retval;
+ if (retval = krb5_cc_store_cred(sudo_context, ccache, &creds)) {
+ (void) krb5_cc_destroy(sudo_context, ccache);
+ return retval;
+ }
+
+ retval = verify_krb_v5_tgt(ccache);
+ (void) krb5_cc_destroy(sudo_context, ccache);
+ return (retval == -1);
+}
+
+
+/*
+ * This routine with some modification is from the MIT V5B6 appl/bsd/login.c
+ *
+ * Verify the Kerberos ticket-granting ticket just retrieved for the
+ * user. If the Kerberos server doesn't respond, assume the user is
+ * trying to fake us out (since we DID just get a TGT from what is
+ * supposedly our KDC). If the host/<host> service is unknown (i.e.,
+ * the local keytab doesn't have it), let her in.
+ *
+ * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
+ */
+static int verify_krb_v5_tgt(ccache)
+ krb5_ccache ccache;
+{
+ char phost[BUFSIZ];
+ krb5_error_code retval;
+ krb5_principal princ;
+ krb5_keyblock * keyblock = 0;
+ krb5_data packet;
+ krb5_auth_context auth_context = NULL;
+
+ packet.data = 0;
+
+ /*
+ * Get the server principal for the local host.
+ * (Use defaults of "host" and canonicalized local name.)
+ */
+ if (krb5_sname_to_principal(sudo_context, NULL, NULL,
+ KRB5_NT_SRV_HST, &princ))
+ return -1;
+
+ /* Extract the name directly. */
+ strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ);
+ phost[BUFSIZ - 1] = '\0';
+
+ /*
+ * Do we have host/<host> keys?
+ * (use default keytab, kvno IGNORE_VNO to get the first match,
+ * and enctype is currently ignored anyhow.)
+ */
+ if (retval = krb5_kt_read_service_key(sudo_context, NULL, princ, 0,
+ ENCTYPE_DES_CBC_MD5, &keyblock)) {
+ /* Keytab or service key does not exist */
+ if (xrealm)
+ retval = -1;
+ else
+ retval = 0;
+ goto cleanup;
+ }
+ if (keyblock)
+ krb5_free_keyblock(sudo_context, keyblock);
+
+ /* Talk to the kdc and construct the ticket. */
+ retval = krb5_mk_req(sudo_context, &auth_context, 0, "host", phost,
+ NULL, ccache, &packet);
+ if (auth_context) {
+ krb5_auth_con_free(sudo_context, auth_context);
+ auth_context = NULL; /* setup for rd_req */
+ }
+ if (retval) {
+ retval = -1;
+ goto cleanup;
+ }
+
+ /* Try to use the ticket. */
+ retval = krb5_rd_req(sudo_context, &auth_context, &packet, princ,
+ NULL, NULL, NULL);
+ if (retval) {
+ retval = -1;
+ } else {
+ retval = 1;
+ }
+
+cleanup:
+ if (packet.data)
+ krb5_free_data_contents(sudo_context, &packet);
+ krb5_free_principal(sudo_context, princ);
+ return retval;
+
+}
+#endif /* HAVE_KERB5 */
+
+
#ifdef HAVE_PAM
/********************************************************************
* pam_attempt_auth()
/* Define if you use Kerberos. */
#undef HAVE_KERB5
-/* Keberos v5 has v4 compatibility */
-#ifdef HAVE_KERB5
-# define HAVE_KERB4
-#endif /* HAVE_KERB5 */
-
/* Define if you use SIA. */
#undef HAVE_SIA
fi
fi
-if test "$with_kerb4" = "yes" -o "$with_kerb5" = "yes"; then
+if test "$with_kerb4" = "yes"; then
if test -f "/usr/include/kerberosIV/krb.h"; then
CPPFLAGS="${CPPFLAGS} -I/usr/include/kerberosIV"
elif test -f "/usr/local/include/kerberosIV/krb.h"; then
elif test -f "/usr/local/kerberos/include/krb.h"; then
CPPFLAGS="${CPPFLAGS} -I/usr/local/kerberos/include"
else
- echo 'Unable to locate kerberos include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS'
+ echo 'Unable to locate kerberos 4 include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS'
fi
if test -d "/usr/kerberos/lib"; then
elif test -f "/usr/local/lib/libkrb.a"; then
SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/lib"
elif test ! -f "/usr/lib/libkrb.a"; then
- echo 'Unable to locate kerberos libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS'
+ echo 'Unable to locate kerberos 4 libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS'
fi
- if test "$with_kerb5" = "yes"; then
- echo $ac_n "checking for -lkrb4""... $ac_c" 1>&6
-echo "configure:6844: checking for -lkrb4" >&5
-if eval "test \"`echo '$''{'ac_cv_lib_krb4'+set}'`\" = set"; then
- echo $ac_n "(cached) $ac_c" 1>&6
-else
- ac_save_LIBS="$LIBS"
-LIBS="-lkrb4 $LIBS"
-cat > conftest.$ac_ext <<EOF
-#line 6851 "configure"
-#include "confdefs.h"
-
-int main() {
-main()
-; return 0; }
-EOF
-if { (eval echo configure:6858: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
- rm -rf conftest*
- ac_cv_lib_krb4=yes
-else
- echo "configure: failed program was:" >&5
- cat conftest.$ac_ext >&5
- rm -rf conftest*
- ac_cv_lib_krb4=no
-fi
-rm -f conftest*
-LIBS="$ac_save_LIBS"
-
-fi
-echo "$ac_t""$ac_cv_lib_krb4" 1>&6
-if test "$ac_cv_lib_krb4" = yes; then
- SUDO_LIBS="${SUDO_LIBS} -lkrb4"
-else
- SUDO_LIBS="${SUDO_LIBS} -lkrb"
-fi
-
- echo $ac_n "checking for -ldes""... $ac_c" 1>&6
-echo "configure:6879: checking for -ldes" >&5
-if eval "test \"`echo '$''{'ac_cv_lib_des'+set}'`\" = set"; then
- echo $ac_n "(cached) $ac_c" 1>&6
-else
- ac_save_LIBS="$LIBS"
-LIBS="-ldes $LIBS"
-cat > conftest.$ac_ext <<EOF
-#line 6886 "configure"
-#include "confdefs.h"
-
-int main() {
-main()
-; return 0; }
-EOF
-if { (eval echo configure:6893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
- rm -rf conftest*
- ac_cv_lib_des=yes
-else
- echo "configure: failed program was:" >&5
- cat conftest.$ac_ext >&5
- rm -rf conftest*
- ac_cv_lib_des=no
-fi
-rm -f conftest*
-LIBS="$ac_save_LIBS"
-
-fi
-echo "$ac_t""$ac_cv_lib_des" 1>&6
-if test "$ac_cv_lib_des" = yes; then
- SUDO_LIBS="${SUDO_LIBS} -ldes"
-fi
-
- SUDO_LIBS="${SUDO_LIBS} -ldes425 -lkrb5 -lcrypto -lcom_err"
- else
- # kerb4
- echo $ac_n "checking for -ldes""... $ac_c" 1>&6
-echo "configure:6915: checking for -ldes" >&5
+ echo $ac_n "checking for -ldes""... $ac_c" 1>&6
+echo "configure:6843: checking for -ldes" >&5
if eval "test \"`echo '$''{'ac_cv_lib_des'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
ac_save_LIBS="$LIBS"
LIBS="-ldes $LIBS"
cat > conftest.$ac_ext <<EOF
-#line 6922 "configure"
+#line 6850 "configure"
#include "confdefs.h"
int main() {
main()
; return 0; }
EOF
-if { (eval echo configure:6929: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
+if { (eval echo configure:6857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then
rm -rf conftest*
ac_cv_lib_des=yes
else
SUDO_LIBS="${SUDO_LIBS} -lkrb"
fi
- fi
+fi
+
+if test "$with_kerb5" = "yes"; then
+ SUDO_LIBS="${SUDO_LIBS} -lkrb5 -lk5crypto -lcom_err"
fi
if test "$with_pam" = "yes"; then
fi
echo $ac_n "checking for log file location""... $ac_c" 1>&6
-echo "configure:7040: checking for log file location" >&5
+echo "configure:6971: checking for log file location" >&5
if test -n "$with_logpath"; then
echo "$ac_t""$with_logpath" 1>&6
cat >> confdefs.h <<EOF
fi
echo $ac_n "checking for timestamp file location""... $ac_c" 1>&6
-echo "configure:7070: checking for timestamp file location" >&5
+echo "configure:7001: checking for timestamp file location" >&5
if test -n "$with_timedir"; then
echo "$ac_t""$with_timedir" 1>&6
cat >> confdefs.h <<EOF
AC_CHECK_LIB(sun, getpwnam, [SUDO_LIBS="${SUDO_LIBS} -lsun"; VISUDO_LIBS="${VISUDO_LIBS} -lsun"; LIBS="${LIBS} -lsun"])
fi
dnl
-dnl Find kerberos includes and libs or complain
+dnl Find kerberos 4 includes and libs or complain
dnl
-if test "$with_kerb4" = "yes" -o "$with_kerb5" = "yes"; then
+if test "$with_kerb4" = "yes"; then
if test -f "/usr/include/kerberosIV/krb.h"; then
CPPFLAGS="${CPPFLAGS} -I/usr/include/kerberosIV"
elif test -f "/usr/local/include/kerberosIV/krb.h"; then
elif test -f "/usr/local/kerberos/include/krb.h"; then
CPPFLAGS="${CPPFLAGS} -I/usr/local/kerberos/include"
else
- echo 'Unable to locate kerberos include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS'
+ echo 'Unable to locate kerberos 4 include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS'
fi
if test -d "/usr/kerberos/lib"; then
elif test -f "/usr/local/lib/libkrb.a"; then
SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/lib"
elif test ! -f "/usr/lib/libkrb.a"; then
- echo 'Unable to locate kerberos libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS'
+ echo 'Unable to locate kerberos 4 libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS'
fi
- if test "$with_kerb5" = "yes"; then
- AC_HAVE_LIBRARY(krb4, SUDO_LIBS="${SUDO_LIBS} -lkrb4", SUDO_LIBS="${SUDO_LIBS} -lkrb")
- AC_HAVE_LIBRARY(des, SUDO_LIBS="${SUDO_LIBS} -ldes")
- SUDO_LIBS="${SUDO_LIBS} -ldes425 -lkrb5 -lcrypto -lcom_err"
- else
- # kerb4
- AC_HAVE_LIBRARY(des, SUDO_LIBS="${SUDO_LIBS} -lkrb -ldes", SUDO_LIBS="${SUDO_LIBS} -lkrb")
- fi
+ AC_HAVE_LIBRARY(des, SUDO_LIBS="${SUDO_LIBS} -lkrb -ldes", SUDO_LIBS="${SUDO_LIBS} -lkrb")
+fi
+
+dnl
+dnl Kerberos 5
+dnl
+if test "$with_kerb5" = "yes"; then
+ SUDO_LIBS="${SUDO_LIBS} -lkrb5 -lk5crypto -lcom_err"
fi
dnl
tty, cwd, runas_user);
break;
+#ifdef HAVE_KERB5
+ case GLOBAL_KRB5_INIT_ERR:
+ (void) sprintf(p, "Could not initialize Kerberos V");
+ break;
+#endif /* HAVE_KERB5 */
+
default:
strcat(p, "found a weird error : ");
break;
#ifdef HAVE_DCE
#include <pthread.h>
#endif /* HAVE_DCE */
+#ifdef HAVE_KERB5
+#include <krb5.h>
+#endif /* HAVE_KERB5 */
#include "sudo.h"
#include "version.h"
extern struct interface *interfaces;
extern int num_interfaces;
extern int printmatches;
+int arg_prompt = 0; /* was -p used? */
+#ifdef HAVE_KERB5
+krb5_context sudo_context = NULL;
+char *realm = NULL;
+int xrealm = 0;
+#endif /* HAVE_KERB5 */
/*
* Table of "bad" envariables to remove and len for strncmp()
#endif /* _AIX */
#ifdef HAVE_KERB4
{ "KRB_CONF", 8 },
-#endif
+#endif /* HAVE_KERB4 */
+#ifdef HAVE_KERB5
+ { "KRB5_CONFIG", 11 },
+#endif /* HAVE_KERB5 */
{ "ENV=", 4 },
{ "BASH_ENV=", 9 },
{ (char *) NULL, 0 }
#ifdef FQDN
struct hostent *h_ent;
#endif /* FQDN */
+#ifdef HAVE_KERB5
+ krb5_error_code retval;
+ char *lrealm;
+#endif /* HAVE_KERB5 */
/*
* Get a local copy of the user's struct passwd with the shadow password
exit(1);
}
+#ifdef HAVE_KERB5
+ if (retval = krb5_init_context(&sudo_context)) {
+ log_error(GLOBAL_KRB5_INIT_ERR);
+ inform_user(GLOBAL_KRB5_INIT_ERR);
+ exit(1);
+ }
+ krb5_init_ets(sudo_context);
+
+ if (retval = krb5_get_default_realm(sudo_context, &lrealm)) {
+ log_error(GLOBAL_KRB5_INIT_ERR);
+ inform_user(GLOBAL_KRB5_INIT_ERR);
+ exit(1);
+ }
+
+ if (realm) {
+ if (strcmp(realm, lrealm) != 0)
+ xrealm = 1; /* User supplied realm is not the system default */
+ free(lrealm);
+ } else
+ realm = lrealm;
+
+ if (!arg_prompt) {
+ p = malloc(strlen(user_name) + strlen(realm) + 17);
+ if (p == NULL) {
+ (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]);
+ exit(1);
+ }
+ sprintf(p, "Password for %s@%s: ", user_name, realm);
+ prompt = p;
+ }
+#endif /* HAVE_KERB5 */
+
/* Set euid == user and ruid == root */
set_perms(PERM_ROOT, sudo_mode);
set_perms(PERM_USER, sudo_mode);
usage(1); /* only one -? option allowed */
switch (NewArgv[0][1]) {
+#ifdef HAVE_KERB5
+ case 'r':
+ /* must have an associated realm */
+ if (NewArgv[1] == NULL)
+ usage(1);
+
+ realm = NewArgv[1];
+
+ /* shift Argv over and adjust Argc */
+ NewArgc--;
+ NewArgv++;
+ break;
+#endif /* HAVE_KERB5 */
case 'p':
/* must have an associated prompt */
if (NewArgv[1] == NULL)
usage(1);
prompt = NewArgv[1];
+ arg_prompt = 1;
/* shift Argv over and adjust Argc */
NewArgc--;
static void usage(exit_val)
int exit_val;
{
- (void) fprintf(stderr, "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] [-u username/#uid] -s | <command>\n", Argv[0]);
+ (void) fprintf(stderr,
+ "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] ",
+ Argv[0]);
+#ifdef HAVE_KERB5
+ (void) fprintf(stderr, "[-r realm] ");
+#endif /* HAVE_KERB5 */
+ (void) fprintf(stderr, "[-u username/#uid] -s | <command>\n");
exit(exit_val);
}
#define BAD_STAMPDIR 0x0E
#define BAD_STAMPFILE 0x0F
#define BAD_ALLOCATION 0x10
+#ifdef HAVE_KERB5
+#define GLOBAL_KRB5_INIT_ERR ( 0x11 | GLOBAL_PROBLEM )
+#endif /* HAVE_KERB5 */
/*
* Boolean values
=head1 SYNOPSIS
B<sudo> B<-V> | B<-h> | B<-l> | B<-v> | B<-k> | B<-s> | B<-H> |
-[ B<-b> ] | [ B<-p> prompt ] [ B<-u> username/#uid] I<command>
+[ B<-b> ] | [ B<-r> realm ] | [ B<-p> prompt ] [ B<-u> username/#uid] I<command>
=head1 DESCRIPTION
command in the background. Note that if you use the C<-b>
option you cannot use shell job control to manipulate the command.
+=item -r
+
+The C<-r> (I<realm>) option is only available if B<sudo> was configured
+with B<Kerberos> version 5 support. It allows the user to specify a
+B<Kerberos> realm other than the system default to use when authenticating
+the user via B<Kerberos>.
+
=item -p
The C<-p> (I<prompt>) option allows you to override the default