int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx) { krb5_keyblock enc_key, *key; krb5_context context; krb5_error_code ret; int i; char *skd, *dkd, *k5err = NULL; int code = -1; if ((ret = krb5_init_context(&context))) { k5err = gssd_k5_err_msg(NULL, ret); printerr(0, "ERROR: initializing krb5_context: %s\n", k5err); goto out_err; } if ((ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &key))){ k5err = gssd_k5_err_msg(context, ret); printerr(0, "ERROR: getting auth_context key: %s\n", k5err); goto out_err_free_context; } memset(&enc_key, 0, sizeof(enc_key)); enc_key.keytype = key->keytype; /* XXX current kernel code only handles des-cbc-raw (4) */ if (enc_key.keytype != 4) { printerr(1, "WARN: write_heimdal_enc_key: " "overriding heimdal keytype (%d => %d)\n", enc_key.keytype, 4); enc_key.keytype = 4; } enc_key.keyvalue.length = key->keyvalue.length; if ((enc_key.keyvalue.data = calloc(1, enc_key.keyvalue.length)) == NULL) { k5err = gssd_k5_err_msg(context, ENOMEM); printerr(0, "ERROR: allocating memory for enc key: %s\n", k5err); goto out_err_free_key; } skd = (char *) key->keyvalue.data; dkd = (char *) enc_key.keyvalue.data; for (i = 0; i < enc_key.keyvalue.length; i++) dkd[i] = skd[i] ^ 0xf0; if (write_heimdal_keyblock(p, end, &enc_key)) { goto out_err_free_enckey; } code = 0; out_err_free_enckey: krb5_free_keyblock_contents(context, &enc_key); out_err_free_key: krb5_free_keyblock(context, key); out_err_free_context: krb5_free_context(context); out_err: free(k5err); printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS"); return(code); }
int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx) { krb5_keyblock *key; krb5_context context; krb5_error_code ret; char *k5err = NULL; int code = -1; if ((ret = krb5_init_context(&context))) { k5err = gssd_k5_err_msg(NULL, ret); printerr(0, "ERROR: initializing krb5_context: %s\n", k5err); goto out_err; } if ((ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &key))){ k5err = gssd_k5_err_msg(context, ret); printerr(0, "ERROR: getting auth_context key: %s\n", k5err); goto out_err_free_context; } /* XXX current kernel code only handles des-cbc-raw (4) */ if (key->keytype != 4) { printerr(1, "WARN: write_heimdal_seq_key: " "overriding heimdal keytype (%d => %d)\n", key->keytype, 4); key->keytype = 4; } if (write_heimdal_keyblock(p, end, key)) { goto out_err_free_key; } code = 0; out_err_free_key: krb5_free_keyblock(context, key); out_err_free_context: krb5_free_context(context); out_err: free(k5err); printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS"); return(code); }
/* * Called upon exit. Destroys machine credentials. */ void gssd_destroy_krb5_machine_creds(void) { krb5_context context; krb5_error_code code = 0; krb5_ccache ccache; struct gssd_k5_kt_princ *ple; char *k5err = NULL; code = krb5_init_context(&context); if (code) { k5err = gssd_k5_err_msg(NULL, code); printerr(0, "ERROR: %s while initializing krb5\n", k5err); goto out; } for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { if (!ple->ccname) continue; if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while resolving credential " "cache '%s' for destruction\n", k5err, ple->ccname); continue; } if ((code = krb5_cc_destroy(context, ccache))) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while destroying credential " "cache '%s'\n", k5err, ple->ccname); } } out: free(k5err); krb5_free_context(context); }
/* * Obtain (or refresh if necessary) Kerberos machine credentials */ int gssd_refresh_krb5_machine_credential(char *hostname, 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[5] = { "$", "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; code = krb5_init_context(&context); if (code) { k5err = gssd_k5_err_msg(NULL, code); printerr(0, "ERROR: %s: %s while initializing krb5 context\n", __func__, k5err); retval = code; gsh_free(k5err); goto out_wo_context; } code = krb5_kt_resolve(context, keytabfile, &kt); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", __func__, k5err, keytabfile); gsh_free(k5err); goto out; } if (ple == NULL) { krb5_keytab_entry 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", __func__, keytabfile, hostname); retval = code; goto out; } ple = get_ple_by_princ(context, kte.principal); k5_free_kt_entry(context, &kte); if (ple == NULL) { char *pname; if ((krb5_unparse_name(context, kte.principal, &pname))) { pname = NULL; } printerr(0, "ERROR: %s: Could not locate or create ple struct for principal %s for connection with host %s\n", __func__, pname ? pname : "<unparsable>", hostname); if (pname) k5_free_unparsed_name(context, pname); goto out; } } retval = gssd_get_single_krb5_cred(context, kt, ple, 0); out: if (kt) krb5_kt_close(context, kt); krb5_free_context(context); out_wo_context: return retval; }
/* * Find a keytab entry to use for a given target hostname. * Tries to find the most appropriate keytab to use given the * name of the host we are trying to connect with. */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; char myhostad[NI_MAXHOST + 1]; int i, j, retval; char *default_realm = NULL; char *realm; char *k5err = NULL; int tried_all = 0, tried_default = 0; krb5_principal princ; /* Get full target hostname */ retval = get_full_hostname(hostname, targethostname, sizeof(targethostname)); if (retval) goto out; /* Get full local hostname */ retval = gethostname(myhostname, sizeof(myhostname)); if (retval) { k5err = gssd_k5_err_msg(context, retval); printerr(1, "%s while getting local hostname\n", k5err); gsh_free(k5err); goto out; } /* Compute the active directory machine name HOST$ */ strcpy(myhostad, myhostname); for (i = 0; myhostad[i] != 0; ++i) myhostad[i] = toupper(myhostad[i]); myhostad[i] = '$'; myhostad[i + 1] = 0; retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); if (retval) goto out; code = krb5_get_default_realm(context, &default_realm); if (code) { retval = code; k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while getting default realm name\n", k5err); gsh_free(k5err); goto out; } /* * Get the realm name(s) for the target hostname. * In reality, this function currently only returns a * single realm, but we code with the assumption that * someday it may actually return a list. */ code = krb5_get_host_realm(context, targethostname, &realmnames); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n", k5err, targethostname); gsh_free(k5err); retval = code; goto out; } /* * Try the "appropriate" realm first, and if nothing found for that * realm, try the default realm (if it hasn't already been tried). */ i = 0; realm = realmnames[i]; while (1) { if (realm == NULL) { tried_all = 1; if (!tried_default) realm = default_realm; } if (tried_all && tried_default) break; if (strcmp(realm, default_realm) == 0) tried_default = 1; for (j = 0; svcnames[j] != NULL; j++) { char spn[300]; /* * The special svcname "$" means 'try the active * directory machine account' */ if (strcmp(svcnames[j], "$") == 0) { snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(myhostad), myhostad, NULL); } else { snprintf(spn, sizeof(spn), "%s/%s@%s", svcnames[j], myhostname, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(svcnames [j]), svcnames[j], strlen(myhostname), myhostname, NULL); } if (code) { k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while building principal for '%s'\n", k5err, spn); gsh_free(k5err); continue; } code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); krb5_free_principal(context, princ); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(3, "%s while getting keytab entry for '%s'\n", k5err, spn); gsh_free(k5err); } else { printerr(3, "Success getting keytab entry for '%s'\n", spn); retval = 0; goto out; } retval = code; } /* * Nothing found with our hostname instance, now look for * names with any instance (they must have an instance) */ for (j = 0; svcnames[j] != NULL; j++) { int found = 0; if (strcmp(svcnames[j], "$") == 0) continue; code = gssd_search_krb5_keytab(context, kt, realm, svcnames[j], &found, kte); if (!code && found) { printerr(3, "Success getting keytab entry for " "%s/*@%s\n", svcnames[j], realm); retval = 0; goto out; } } if (!tried_all) { i++; realm = realmnames[i]; } } out: if (default_realm) k5_free_default_realm(context, default_realm); if (realmnames) krb5_free_host_realm(context, realmnames); return retval; }
/* * Search the given keytab file looking for an entry with the given * service name and realm, ignoring hostname (instance). * * Returns: * 0 => No error * non-zero => An error occurred * * If a keytab entry is found, "found" is set to one, and the keytab * entry is returned in "kte". Otherwise, "found" is zero, and the * value of "kte" is unpredictable. */ static int gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, const char *realm, const char *service, int *found, krb5_keytab_entry *kte) { krb5_kt_cursor cursor; krb5_error_code code; struct gssd_k5_kt_princ *ple; int retval = -1, status; char kt_name[BUFSIZ]; char *pname; char *k5err = NULL; if (found == NULL) { retval = EINVAL; goto out; } *found = 0; /* * Look through each entry in the keytab file and determine * if we might want to use it as machine credentials. If so, * save info in the global principal list (gssd_k5_kt_princ_list). */ code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s attempting to get keytab name\n", k5err); gsh_free(k5err); retval = code; goto out; } code = krb5_kt_start_seq_get(context, kt, &cursor); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while beginning keytab scan " "for keytab '%s'\n", k5err, kt_name); gsh_free(k5err); retval = code; goto out; } while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) { code = krb5_unparse_name(context, kte->principal, &pname); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: Skipping keytab entry because " "we failed to unparse principal name: %s\n", k5err); k5_free_kt_entry(context, kte); gsh_free(k5err); continue; } printerr(4, "Processing keytab entry for principal '%s'\n", pname); /* Use the first matching keytab entry found */ #ifdef HAVE_KRB5 status = realm_and_service_match(kte->principal, realm, service); #else status = realm_and_service_match(context, kte->principal, realm, service); #endif if (status) { printerr(4, "We WILL use this entry (%s)\n", pname); ple = get_ple_by_princ(context, kte->principal); /* * Return, don't free, keytab entry if * we were successful! */ if (ple == NULL) { retval = ENOMEM; k5_free_kt_entry(context, kte); } else { retval = 0; *found = 1; } k5_free_unparsed_name(context, pname); break; } else { printerr(4, "We will NOT use this entry (%s)\n", pname); } k5_free_unparsed_name(context, pname); k5_free_kt_entry(context, kte); } code = krb5_kt_end_seq_get(context, kt, &cursor); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while ending keytab scan for " "keytab '%s'\n", k5err, kt_name); gsh_free(k5err); } retval = 0; out: return retval; }
/* * Obtain credentials via a key in the keytab given * a keytab handle and a gssd_k5_kt_princ structure. * Checks to see if current credentials are expired, * if not, uses the keytab to obtain new credentials. * * Returns: * 0 => success (or credentials have not expired) * nonzero => error */ static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, 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; #else krb5_get_init_creds_opt options; #endif krb5_get_init_creds_opt *opts; krb5_creds my_creds; krb5_ccache ccache = NULL; char kt_name[BUFSIZ]; char cc_name[BUFSIZ]; int code; time_t now = time(0); char *cache_type; char *pname = NULL; char *k5err = NULL; memset(&my_creds, 0, sizeof(my_creds)); 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; goto out; } code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ); if (code != 0) { printerr(0, "ERROR: Unable to get keytab name in " "gssd_get_single_krb5_cred\n"); goto out; } if ((krb5_unparse_name(context, ple->princ, &pname))) pname = NULL; #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS code = krb5_get_init_creds_opt_alloc(context, &init_opts); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s allocating gic options\n", k5err); goto out; } if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1)) printerr(1, "WARNING: Unable to set option for addressless " "tickets. May have problems behind a NAT.\n"); #ifdef TEST_SHORT_LIFETIME /* set a short lifetime (for debugging only!) */ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); krb5_get_init_creds_opt_set_tkt_life(init_opts, 5 * 60); #endif opts = init_opts; #else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */ krb5_get_init_creds_opt_init(&options); krb5_get_init_creds_opt_set_address_list(&options, NULL); #ifdef TEST_SHORT_LIFETIME /* set a short lifetime (for debugging only!) */ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); krb5_get_init_creds_opt_set_tkt_life(&options, 5 * 60); #endif opts = &options; #endif code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ, kt, 0, NULL, opts); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(1, "WARNING: %s while getting initial ticket for " "principal '%s' using keytab '%s'\n", k5err, pname ? pname : "<unparsable>", kt_name); goto out; } /* * Initialize cache file which we're going to be using */ if (use_memcache) cache_type = "MEMORY"; else cache_type = "FILE"; snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s", cache_type, ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); ple->endtime = my_creds.times.endtime; if (ple->ccname != NULL) gsh_free(ple->ccname); ple->ccname = gsh_strdup(cc_name); if (ple->ccname == NULL) { printerr(0, "ERROR: no storage to duplicate credentials " "cache name '%s'\n", cc_name); code = ENOMEM; goto out; } code = krb5_cc_resolve(context, cc_name, &ccache); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while opening credential cache '%s'\n", k5err, cc_name); goto out; } code = krb5_cc_initialize(context, ccache, ple->princ); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while initializing credential " "cache '%s'\n", k5err, cc_name); } code = krb5_cc_store_cred(context, ccache, &my_creds); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while storing credentials in '%s'\n", k5err, cc_name); goto out; } /* if we get this far, let gss mech know */ gssd_set_krb5_ccache_name(cc_name); code = 0; printerr(2, "Successfully obtained machine credentials for " "principal '%s' stored in ccache '%s'\n", pname, cc_name); out: #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS if (init_opts) krb5_get_init_creds_opt_free(context, init_opts); #endif if (pname) k5_free_unparsed_name(context, pname); if (ccache) krb5_cc_close(context, ccache); krb5_free_cred_contents(context, &my_creds); gsh_free(k5err); return code; }
/* * Obtain (or refresh if necessary) Kerberos machine credentials */ int gssd_refresh_krb5_machine_credential(char *hostname, struct gssd_k5_kt_princ *ple) { krb5_error_code code = 0; krb5_context context; krb5_keytab kt = NULL;; int retval = 0; char *k5err = NULL; if (hostname == NULL && ple == NULL) return EINVAL; code = krb5_init_context(&context); if (code) { k5err = gssd_k5_err_msg(NULL, code); printerr(0, "ERROR: %s: %s while initializing krb5 context\n", __func__, k5err); retval = code; goto out; } if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", __func__, k5err, keytabfile); goto out; } if (ple == NULL) { krb5_keytab_entry kte; code = find_keytab_entry(context, kt, hostname, &kte); if (code) { printerr(0, "ERROR: %s: no usable keytab entry found " "in keytab %s for connection with host %s\n", __FUNCTION__, keytabfile, hostname); retval = code; goto out; } ple = get_ple_by_princ(context, kte.principal); k5_free_kt_entry(context, &kte); if (ple == NULL) { char *pname; if ((krb5_unparse_name(context, kte.principal, &pname))) { pname = NULL; } printerr(0, "ERROR: %s: Could not locate or create " "ple struct for principal %s for connection " "with host %s\n", __FUNCTION__, pname ? pname : "<unparsable>", hostname); if (pname) k5_free_unparsed_name(context, pname); goto out; } } retval = gssd_get_single_krb5_cred(context, kt, ple); out: if (kt) krb5_kt_close(context, kt); krb5_free_context(context); free(k5err); return retval; }