/*
- * Copyright (c) 1999, 2001 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
+ * Copyright (c) 1999-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
- * This code is derived from software contributed by Frank Cusack
- * <fcusack@fcusack.com>.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * 4. Products derived from this software may not be called "Sudo" nor
- * may "Sudo" appear in their names without specific prior written
- * permission from the author.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
-#include "config.h"
+#include <config.h>
#include <sys/types.h>
#include <sys/param.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <krb5.h>
+#ifdef HAVE_HEIMDAL
+#include <com_err.h>
+#endif
#include "sudo.h"
#include "sudo_auth.h"
#ifndef lint
-static const char rcsid[] = "$Sudo: kerb5.c,v 1.11 2001/12/14 19:52:53 millert Exp $";
+__unused static const char rcsid[] = "$Sudo: kerb5.c,v 1.23.2.4 2007/06/12 01:28:42 millert Exp $";
#endif /* lint */
+#ifdef HAVE_HEIMDAL
+# define extract_name(c, p) krb5_principal_get_comp_string(c, p, 1)
+# define krb5_free_data_contents(c, d) krb5_data_free(d)
+#else
+# define extract_name(c, p) (krb5_princ_component(c, p, 1)->data)
+#endif
+
+#ifndef HAVE_KRB5_VERIFY_USER
static int verify_krb_v5_tgt __P((krb5_context, krb5_ccache, char *));
+#endif
static struct _sudo_krb5_data {
krb5_context sudo_context;
krb5_principal princ;
} sudo_krb5_data = { NULL, NULL, NULL };
typedef struct _sudo_krb5_data *sudo_krb5_datap;
-extern krb5_cc_ops krb5_mcc_ops;
+extern const krb5_cc_ops krb5_mcc_ops;
int
kerb5_init(pw, promptp, auth)
auth->data = (VOID *) &sudo_krb5_data; /* Stash all our data here */
- if (error = krb5_init_context(&(sudo_krb5_data.sudo_context))) {
- log_error(NO_EXIT|NO_MAIL,
- "%s: unable to initialize context: %s", auth->name,
- error_message(error));
+#ifdef HAVE_KRB5_INIT_SECURE_CONTEXT
+ error = krb5_init_secure_context(&(sudo_krb5_data.sudo_context));
+#else
+ error = krb5_init_context(&(sudo_krb5_data.sudo_context));
+#endif
+ if (error)
return(AUTH_FAILURE);
- }
sudo_context = sudo_krb5_data.sudo_context;
- if (error = krb5_parse_name(sudo_context, pw->pw_name,
- &(sudo_krb5_data.princ))) {
- log_error(NO_EXIT|NO_MAIL,
+ if ((error = krb5_parse_name(sudo_context, pw->pw_name,
+ &(sudo_krb5_data.princ)))) {
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to parse '%s': %s", auth->name, pw->pw_name,
error_message(error));
return(AUTH_FAILURE);
* The API does not currently provide this unless the auth is standalone.
*/
#if 1
- if (error = krb5_unparse_name(sudo_context, princ, &pname)) {
+ if ((error = krb5_unparse_name(sudo_context, princ, &pname))) {
log_error(NO_EXIT|NO_MAIL,
"%s: unable to unparse princ ('%s'): %s", auth->name,
pw->pw_name, error_message(error));
#endif
/* For CNS compatibility */
- if (error = krb5_cc_register(sudo_context, &krb5_mcc_ops, FALSE)) {
+ if ((error = krb5_cc_register(sudo_context, &krb5_mcc_ops, FALSE))) {
if (error != KRB5_CC_TYPE_EXISTS) {
- log_error(NO_EXIT|NO_MAIL,
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to use Memory ccache: %s", auth->name,
error_message(error));
return(AUTH_FAILURE);
(void) snprintf(cache_name, sizeof(cache_name), "MEMORY:sudocc_%ld",
(long) getpid());
- if (error = krb5_cc_resolve(sudo_context, cache_name,
- &(sudo_krb5_data.ccache))) {
- log_error(NO_EXIT|NO_MAIL,
+ if ((error = krb5_cc_resolve(sudo_context, cache_name,
+ &(sudo_krb5_data.ccache)))) {
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to resolve ccache: %s", auth->name,
error_message(error));
return(AUTH_FAILURE);
}
ccache = sudo_krb5_data.ccache;
- if (error = krb5_cc_initialize(sudo_context, ccache, princ)) {
- log_error(NO_EXIT|NO_MAIL,
+ if ((error = krb5_cc_initialize(sudo_context, ccache, princ))) {
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to initialize ccache: %s", auth->name,
error_message(error));
return(AUTH_FAILURE);
return(AUTH_SUCCESS);
}
+#ifdef HAVE_KRB5_VERIFY_USER
+int
+kerb5_verify(pw, pass, auth)
+ struct passwd *pw;
+ char *pass;
+ sudo_auth *auth;
+{
+ krb5_context sudo_context;
+ krb5_principal princ;
+ krb5_ccache ccache;
+ krb5_error_code error;
+
+ sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
+ princ = ((sudo_krb5_datap) auth->data)->princ;
+ ccache = ((sudo_krb5_datap) auth->data)->ccache;
+
+ error = krb5_verify_user(sudo_context, princ, ccache, pass, 1, NULL);
+ return (error ? AUTH_FAILURE : AUTH_SUCCESS);
+}
+#else
int
kerb5_verify(pw, pass, auth)
struct passwd *pw;
krb5_creds creds;
krb5_error_code error;
krb5_get_init_creds_opt opts;
- char cache_name[64];
sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context;
princ = ((sudo_krb5_datap) auth->data)->princ;
krb5_get_init_creds_opt_init(&opts);
/* Note that we always obtain a new TGT to verify the user */
- if (error = krb5_get_init_creds_password(sudo_context, &creds, princ,
+ if ((error = krb5_get_init_creds_password(sudo_context, &creds, princ,
pass, krb5_prompter_posix,
- NULL, 0, NULL, &opts)) {
+ NULL, 0, NULL, &opts))) {
if (error == KRB5KRB_AP_ERR_BAD_INTEGRITY) /* Bad password */
return(AUTH_FAILURE);
/* Some other error */
- log_error(NO_EXIT|NO_MAIL,
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to get credentials: %s", auth->name,
error_message(error));
return(AUTH_FAILURE);
}
/* Stash the TGT so we can verify it. */
- if (error = krb5_cc_store_cred(sudo_context, ccache, &creds)) {
- log_error(NO_EXIT|NO_MAIL,
+ if ((error = krb5_cc_store_cred(sudo_context, ccache, &creds))) {
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to store credentials: %s", auth->name,
error_message(error));
} else {
krb5_free_cred_contents(sudo_context, &creds);
return (error ? AUTH_FAILURE : AUTH_SUCCESS);
}
+#endif
int
kerb5_cleanup(pw, auth)
return(AUTH_SUCCESS);
}
+#ifndef HAVE_KRB5_VERIFY_USER
/*
* This routine with some modification is from the MIT V5B6 appl/bsd/login.c
*
* Get the server principal for the local host.
* (Use defaults of "host" and canonicalized local name.)
*/
- if (error = krb5_sname_to_principal(sudo_context, NULL, NULL,
- KRB5_NT_SRV_HST, &princ)) {
- log_error(NO_EXIT|NO_MAIL,
+ if ((error = krb5_sname_to_principal(sudo_context, NULL, NULL,
+ KRB5_NT_SRV_HST, &princ))) {
+ log_error(NO_EXIT|NO_MAIL,
"%s: unable to get host principal: %s", auth_name,
error_message(error));
return(-1);
}
/* Extract the name directly. Yow. */
- strncpy(phost, krb5_princ_component(sudo_context, princ, 1)->data,
- sizeof(phost) - 1);
- phost[sizeof(phost) - 1] = '\0';
+ strlcpy(phost, extract_name(sudo_context, princ), sizeof(phost));
/*
* Do we have host/<host> keys?
* (use default keytab, kvno IGNORE_VNO to get the first match,
* and enctype is currently ignored anyhow.)
*/
- if (error = krb5_kt_read_service_key(sudo_context, NULL, princ, 0,
- ENCTYPE_DES_CBC_MD5, &keyblock)) {
+ if ((error = krb5_kt_read_service_key(sudo_context, NULL, princ, 0,
+ 0, &keyblock))) {
/* Keytab or service key does not exist. */
log_error(NO_EXIT,
"%s: host service key not found: %s", auth_name,
error_message(error));
- error = 0;
goto cleanup;
}
if (keyblock)
krb5_free_principal(sudo_context, princ);
if (error)
- log_error(NO_EXIT|NO_MAIL,
+ log_error(NO_EXIT|NO_MAIL,
"%s: Cannot verify TGT! Possible attack!: %s", auth_name,
error_message(error));
return(error);
}
+#endif