X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=utils%2Fgssd%2Fkrb5_util.c;h=d23654ff1b9c581ebb713370efc8d93f88158f1a;hp=3009cc57b24f00ec83393b55e32578eb05b49eb2;hb=12af21aca517dfbaec465447d8336bcc4769f71d;hpb=38062464a09868ff579b3c3c0b43fcd550ea3c61 diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 3009cc5..d23654f 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -137,7 +137,7 @@ static int select_krb5_ccache(const struct dirent *d); static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d); static int gssd_get_single_krb5_cred(krb5_context context, - krb5_keytab kt, struct gssd_k5_kt_princ *ple); + krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); static int query_krb5_ccache(const char* cred_cache, char **ret_princname, char **ret_realm); @@ -170,9 +170,8 @@ select_krb5_ccache(const struct dirent *d) * what we want. Otherwise, return zero and no dirent pointer. * The caller is responsible for freeing the dirent if one is returned. * - * Returns: - * 0 => could not find an existing entry - * 1 => found an existing entry + * Returns 0 if a valid-looking entry was found and a non-zero error + * code otherwise. */ static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) @@ -186,7 +185,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) char buf[1030]; char *princname = NULL; char *realm = NULL; - int score, best_match_score = 0; + int score, best_match_score = 0, err = -EACCES; memset(&best_match_stat, 0, sizeof(best_match_stat)); *d = NULL; @@ -225,10 +224,18 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) free(namelist[i]); continue; } + if (uid == 0 && !root_uses_machine_creds && + strstr(namelist[i]->d_name, "_machine_")) { + printerr(3, "CC file '%s' not available to root\n", + statname); + free(namelist[i]); + continue; + } if (!query_krb5_ccache(buf, &princname, &realm)) { printerr(3, "CC file '%s' is expired or corrupt\n", statname); free(namelist[i]); + err = -EKEYEXPIRED; continue; } @@ -284,67 +291,13 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) } free(namelist); } - if (found) - { + if (found) { *d = best_match_dir; + return 0; } - return found; -} - - -#ifdef HAVE_SET_ALLOWABLE_ENCTYPES -/* - * this routine obtains a credentials handle via gss_acquire_cred() - * then calls gss_krb5_set_allowable_enctypes() to limit the encryption - * types negotiated. - * - * XXX Should call some function to determine the enctypes supported - * by the kernel. (Only need to do that once!) - * - * Returns: - * 0 => all went well - * -1 => there was an error - */ - -int -limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) -{ - u_int maj_stat, min_stat; - gss_cred_id_t credh; - gss_OID_set_desc desired_mechs; - krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, - ENCTYPE_DES_CBC_MD5, - ENCTYPE_DES_CBC_MD4 }; - int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); - - /* We only care about getting a krb5 cred */ - desired_mechs.count = 1; - desired_mechs.elements = &krb5oid; - - maj_stat = gss_acquire_cred(&min_stat, NULL, 0, - &desired_mechs, GSS_C_INITIATE, - &credh, NULL, NULL); - - if (maj_stat != GSS_S_COMPLETE) { - if (get_verbosity() > 0) - pgsserr("gss_acquire_cred", - maj_stat, min_stat, &krb5oid); - return -1; - } - - maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, - num_enctypes, &enctypes); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_set_allowable_enctypes", - maj_stat, min_stat, &krb5oid); - gss_release_cred(&min_stat, &credh); - return -1; - } - sec->cred = credh; - return 0; + return err; } -#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ /* * Obtain credentials via a key in the keytab given @@ -359,7 +312,8 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, + int nocache) { #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS krb5_get_init_creds_opt *init_opts = NULL; @@ -379,7 +333,7 @@ gssd_get_single_krb5_cred(krb5_context context, memset(&my_creds, 0, sizeof(my_creds)); - if (ple->ccname && ple->endtime > now) { + if (ple->ccname && ple->endtime > now && !nocache) { printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", ple->ccname, ple->endtime); code = 0; @@ -796,10 +750,9 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, - krb5_keytab_entry *kte) + krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; - const char *svcnames[] = { "root", "nfs", "host", NULL }; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; int i, j, retval; @@ -1024,29 +977,29 @@ err_cache: * given only a UID. We really need more information, but we * do the best we can. * - * Returns: - * 0 => a ccache was found - * 1 => no ccache was found + * Returns 0 if a ccache was found, and a non-zero error code otherwise. */ int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname) { char buf[MAX_NETOBJ_SZ]; struct dirent *d; + int err; printerr(2, "getting credentials for client with uid %u for " "server %s\n", uid, servername); memset(buf, 0, sizeof(buf)); - if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) { - snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name); - free(d); - } - else - return 1; + err = gssd_find_existing_krb5_ccache(uid, dirname, &d); + if (err) + return err; + + snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name); + free(d); + printerr(2, "using %s as credentials cache for client with " "uid %u for server %s\n", buf, uid, servername); gssd_set_krb5_ccache_name(buf); - return 0; + return err; } /* @@ -1095,7 +1048,8 @@ gssd_get_krb5_machine_cred_list(char ***list) for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { if (ple->ccname) { /* Make sure cred is up-to-date before returning it */ - retval = gssd_refresh_krb5_machine_credential(NULL, ple); + retval = gssd_refresh_krb5_machine_credential(NULL, ple, + NULL); if (retval) continue; if (i + 1 > listsize) { @@ -1185,14 +1139,24 @@ gssd_destroy_krb5_machine_creds(void) */ int gssd_refresh_krb5_machine_credential(char *hostname, - struct gssd_k5_kt_princ *ple) + struct gssd_k5_kt_princ *ple, + char *service) { krb5_error_code code = 0; krb5_context context; krb5_keytab kt = NULL;; int retval = 0; char *k5err = NULL; + const char *svcnames[4] = { "root", "nfs", "host", NULL }; + /* + * If a specific service name was specified, use it. + * Otherwise, use the default list. + */ + if (service != NULL && strcmp(service, "*") != 0) { + svcnames[0] = service; + svcnames[1] = NULL; + } if (hostname == NULL && ple == NULL) return EINVAL; @@ -1215,7 +1179,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, if (ple == NULL) { krb5_keytab_entry kte; - code = find_keytab_entry(context, kt, hostname, &kte); + code = find_keytab_entry(context, kt, hostname, &kte, svcnames); if (code) { printerr(0, "ERROR: %s: no usable keytab entry found " "in keytab %s for connection with host %s\n", @@ -1240,7 +1204,7 @@ gssd_refresh_krb5_machine_credential(char *hostname, goto out; } } - retval = gssd_get_single_krb5_cred(context, kt, ple); + retval = gssd_get_single_krb5_cred(context, kt, ple, 0); out: if (kt) krb5_kt_close(context, kt); @@ -1292,3 +1256,68 @@ gssd_k5_get_default_realm(char **def_realm) krb5_free_context(context); } + +#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +/* + * this routine obtains a credentials handle via gss_acquire_cred() + * then calls gss_krb5_set_allowable_enctypes() to limit the encryption + * types negotiated. + * + * XXX Should call some function to determine the enctypes supported + * by the kernel. (Only need to do that once!) + * + * Returns: + * 0 => all went well + * -1 => there was an error + */ + +int +limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) +{ + u_int maj_stat, min_stat; + gss_cred_id_t credh; + gss_OID_set_desc desired_mechs; + krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; + int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); + extern int num_krb5_enctypes; + extern krb5_enctype *krb5_enctypes; + + /* We only care about getting a krb5 cred */ + desired_mechs.count = 1; + desired_mechs.elements = &krb5oid; + + maj_stat = gss_acquire_cred(&min_stat, NULL, 0, + &desired_mechs, GSS_C_INITIATE, + &credh, NULL, NULL); + + if (maj_stat != GSS_S_COMPLETE) { + if (get_verbosity() > 0) + pgsserr("gss_acquire_cred", + maj_stat, min_stat, &krb5oid); + return -1; + } + + /* + * If we failed for any reason to produce global + * list of supported enctypes, use local default here. + */ + if (krb5_enctypes == NULL) + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_enctypes, enctypes); + else + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_krb5_enctypes, krb5_enctypes); + + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_set_allowable_enctypes", + maj_stat, min_stat, &krb5oid); + gss_release_cred(&min_stat, &credh); + return -1; + } + sec->cred = credh; + + return 0; +} +#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */