]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/krb5_util.c
Always get addressless tickets
[nfs-utils.git] / utils / gssd / krb5_util.c
index 5d433b121ee26b96f2319fec04eda6eca638dc18..bf8690e3fbd325ca12ac6e7c608d82f91f197a62 100644 (file)
@@ -134,9 +134,6 @@ static int select_krb5_ccache(const struct dirent *d);
 static int gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d);
 static int gssd_get_single_krb5_cred(krb5_context context,
                krb5_keytab kt, struct gssd_k5_kt_princ *ple);
-static int gssd_have_realm_ple(void *realm);
-static int gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt,
-               char *kt_name);
 
 
 /*
@@ -326,7 +323,12 @@ gssd_get_single_krb5_cred(krb5_context context,
                          krb5_keytab kt,
                          struct gssd_k5_kt_princ *ple)
 {
+#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];
@@ -354,19 +356,40 @@ gssd_get_single_krb5_cred(krb5_context context,
        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) {
+               printerr(0, "ERROR: %s allocating gic options\n",
+                        gssd_k5_err_msg(context, code));
+               goto out;
+       }
+       if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1))
+               printerr(0, "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
+
        if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ,
-                                              kt, 0, NULL, &options))) {
+                                              kt, 0, NULL, opts))) {
                printerr(0, "WARNING: %s while getting initial ticket for "
                         "principal '%s' using keytab '%s'\n",
-                        error_message(code),
+                        gssd_k5_err_msg(context, code),
                         pname ? pname : "<unparsable>", kt_name);
                goto out;
        }
@@ -395,17 +418,18 @@ gssd_get_single_krb5_cred(krb5_context context,
        }
        if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
                printerr(0, "ERROR: %s while opening credential cache '%s'\n",
-                        error_message(code), cc_name);
+                        gssd_k5_err_msg(context, code), cc_name);
                goto out;
        }
        if ((code = krb5_cc_initialize(context, ccache, ple->princ))) {
                printerr(0, "ERROR: %s while initializing credential "
-                        "cache '%s'\n", error_message(code), cc_name);
+                        "cache '%s'\n", gssd_k5_err_msg(context, code),
+                        cc_name);
                goto out;
        }
        if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) {
                printerr(0, "ERROR: %s while storing credentials in '%s'\n",
-                        error_message(code), cc_name);
+                        gssd_k5_err_msg(context, code), cc_name);
                goto out;
        }
 
@@ -413,6 +437,10 @@ gssd_get_single_krb5_cred(krb5_context context,
        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)
@@ -421,147 +449,6 @@ gssd_get_single_krb5_cred(krb5_context context,
        return (code);
 }
 
-/*
- * Determine if we already have a ple for the given realm
- *
- * Returns:
- *     0 => no ple found for given realm
- *     1 => found ple for given realm
- */
-static int
-gssd_have_realm_ple(void *r)
-{
-       struct gssd_k5_kt_princ *ple;
-#ifdef HAVE_KRB5
-       krb5_data *realm = (krb5_data *)r;
-#else
-       char *realm = (char *)r;
-#endif
-
-       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-#ifdef HAVE_KRB5
-               if ((realm->length == strlen(ple->realm)) &&
-                   (strncmp(realm->data, ple->realm, realm->length) == 0)) {
-#else
-               if (strcmp(realm, ple->realm) == 0) {
-#endif
-                   return 1;
-               }
-       }
-       return 0;
-}
-
-/*
- * Process the given keytab file and create a list of principals we
- * might use as machine credentials.
- *
- * Returns:
- *     0 => Sucess
- *     nonzero => Error
- */
-static int
-gssd_process_krb5_keytab(krb5_context context, krb5_keytab kt, char *kt_name)
-{
-       krb5_kt_cursor cursor;
-       krb5_keytab_entry kte;
-       krb5_error_code code;
-       struct gssd_k5_kt_princ *ple;
-       int retval = -1;
-
-       /*
-        * 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).
-        * Note: (ple == principal list entry)
-        */
-       if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
-               printerr(0, "ERROR: %s while beginning keytab scan "
-                           "for keytab '%s'\n",
-                       error_message(code), kt_name);
-               retval = code;
-               goto out;
-       }
-
-       while ((code = krb5_kt_next_entry(context, kt, &kte, &cursor)) == 0) {
-               char *pname;
-               if ((code = krb5_unparse_name(context, kte.principal,
-                                             &pname))) {
-                       printerr(0, "WARNING: Skipping keytab entry because "
-                                   "we failed to unparse principal name: %s\n",
-                                error_message(code));
-                       krb5_kt_free_entry(context, &kte);
-                       continue;
-               }
-               printerr(2, "Processing keytab entry for principal '%s'\n",
-                        pname);
-               /* Just use the first keytab entry found for each realm */
-               if ((!gssd_have_realm_ple((void *)&kte.principal->realm)) ) {
-                       printerr(2, "We WILL use this entry (%s)\n", pname);
-                       ple = malloc(sizeof(struct gssd_k5_kt_princ));
-                       if (ple == NULL) {
-                               printerr(0, "ERROR: could not allocate storage "
-                                           "for principal list entry\n");
-                               k5_free_unparsed_name(context, pname);
-                               krb5_kt_free_entry(context, &kte);
-                               retval = ENOMEM;
-                               goto out;
-                       }
-                       /* These will be filled in later */
-                       ple->next = NULL;
-                       ple->ccname = NULL;
-                       ple->endtime = 0;
-                       if ((ple->realm =
-#ifdef HAVE_KRB5
-                               strndup(kte.principal->realm.data,
-                                       kte.principal->realm.length))
-#else
-                               strdup(kte.principal->realm))
-#endif
-                                       == NULL) {
-                               printerr(0, "ERROR: %s while copying realm to "
-                                           "principal list entry\n",
-                                        "not enough memory");
-                               k5_free_unparsed_name(context, pname);
-                               krb5_kt_free_entry(context, &kte);
-                               retval = ENOMEM;
-                               goto out;
-                       }
-                       if ((code = krb5_copy_principal(context,
-                                       kte.principal, &ple->princ))) {
-                               printerr(0, "ERROR: %s while copying principal "
-                                           "to principal list entry\n",
-                                       error_message(code));
-                               k5_free_unparsed_name(context, pname);
-                               krb5_kt_free_entry(context, &kte);
-                               retval = code;
-                               goto out;
-                       }
-                       if (gssd_k5_kt_princ_list == NULL)
-                               gssd_k5_kt_princ_list = ple;
-                       else {
-                               ple->next = gssd_k5_kt_princ_list;
-                               gssd_k5_kt_princ_list = ple;
-                       }
-               }
-               else {
-                       printerr(2, "We will NOT use this entry (%s)\n",
-                               pname);
-               }
-               k5_free_unparsed_name(context, pname);
-               krb5_kt_free_entry(context, &kte);
-       }
-
-       if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
-               printerr(0, "WARNING: %s while ending keytab scan for "
-                           "keytab '%s'\n",
-                        error_message(code), kt_name);
-       }
-
-       retval = 0;
-  out:
-       return retval;
-}
-
 /*
  * Depending on the version of Kerberos, we either need to use
  * a private function, or simply set the environment variable.
@@ -796,14 +683,14 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
         */
        if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
                printerr(0, "ERROR: %s attempting to get keytab name\n",
-                        error_message(code));
+                        gssd_k5_err_msg(context, code));
                retval = code;
                goto out;
        }
        if ((code = krb5_kt_start_seq_get(context, kt, &cursor))) {
                printerr(0, "ERROR: %s while beginning keytab scan "
                            "for keytab '%s'\n",
-                       error_message(code), kt_name);
+                       gssd_k5_err_msg(context, code), kt_name);
                retval = code;
                goto out;
        }
@@ -813,7 +700,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
                                              &pname))) {
                        printerr(0, "WARNING: Skipping keytab entry because "
                                 "we failed to unparse principal name: %s\n",
-                                error_message(code));
+                                gssd_k5_err_msg(context, code));
                        k5_free_kt_entry(context, kte);
                        continue;
                }
@@ -849,7 +736,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
        if ((code = krb5_kt_end_seq_get(context, kt, &cursor))) {
                printerr(0, "WARNING: %s while ending keytab scan for "
                            "keytab '%s'\n",
-                        error_message(code), kt_name);
+                        gssd_k5_err_msg(context, code), kt_name);
        }
 
        retval = 0;
@@ -887,7 +774,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
        retval = gethostname(myhostname, sizeof(myhostname));
        if (retval) {
                printerr(1, "%s while getting local hostname\n",
-                        error_message(retval));
+                        gssd_k5_err_msg(context, retval));
                goto out;
        }
        retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
@@ -898,7 +785,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
        if (code) {
                retval = code;
                printerr(1, "%s while getting default realm name\n",
-                        error_message(code));
+                        gssd_k5_err_msg(context, code));
                goto out;
        }
 
@@ -911,7 +798,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
        code = krb5_get_host_realm(context, targethostname, &realmnames);
        if (code) {
                printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n",
-                        error_message(code), targethostname);
+                        gssd_k5_err_msg(context, code), targethostname);
                retval = code;
                goto out;
        }
@@ -943,7 +830,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
                                                        NULL);
                        if (code) {
                                printerr(1, "%s while building principal for "
-                                        "'%s/%s@%s'\n", error_message(code),
+                                        "'%s/%s@%s'\n",
+                                        gssd_k5_err_msg(context, code),
                                         svcnames[j], myhostname, realm);
                                continue;
                        }
@@ -951,7 +839,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
                        krb5_free_principal(context, princ);
                        if (code) {
                                printerr(3, "%s while getting keytab entry for "
-                                        "'%s/%s@%s'\n", error_message(code),
+                                        "'%s/%s@%s'\n",
+                                        gssd_k5_err_msg(context, code),
                                         svcnames[j], myhostname, realm);
                        } else {
                                printerr(3, "Success getting keytab entry for "
@@ -1038,96 +927,6 @@ gssd_setup_krb5_machine_gss_ccache(char *ccname)
        gssd_set_krb5_ccache_name(ccname);
 }
 
-/*
- * The first time through this routine, go through the keytab and
- * determine which keys we will try to use as machine credentials.
- * Every time through this routine, try to obtain credentials using
- * the keytab entries selected the first time through.
- *
- * Returns:
- *     0 => obtained one or more credentials
- *     nonzero => error
- *
- */
-
-int
-gssd_refresh_krb5_machine_creds(void)
-{
-       krb5_context context = NULL;
-       krb5_keytab kt = NULL;;
-       krb5_error_code code;
-       int retval = -1;
-       struct gssd_k5_kt_princ *ple;
-       int gotone = 0;
-       static int processed_keytab = 0;
-
-
-       code = krb5_init_context(&context);
-       if (code) {
-               printerr(0, "ERROR: %s while initializing krb5 in "
-                           "gssd_refresh_krb5_machine_creds\n",
-                        error_message(code));
-               retval = code;
-               goto out;
-       }
-
-       printerr(1, "Using keytab file '%s'\n", keytabfile);
-
-       if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
-               printerr(0, "ERROR: %s while resolving keytab '%s'\n",
-                        error_message(code), keytabfile);
-               goto out;
-       }
-
-       /* Only go through the keytab file once.  Only print messages once. */
-       if (gssd_k5_kt_princ_list == NULL && !processed_keytab) {
-               processed_keytab = 1;
-               gssd_process_krb5_keytab(context, kt, keytabfile);
-               if (gssd_k5_kt_princ_list == NULL) {
-                       printerr(0, "ERROR: No usable keytab entries found in "
-                                   "keytab '%s'\n", keytabfile);
-                       printerr(0, "Do you have a valid keytab entry for "
-                                   "%s/<your.host>@<YOUR.REALM> in "
-                                   "keytab file %s ?\n",
-                                   GSSD_SERVICE_NAME, keytabfile);
-                       printerr(0, "Continuing without (machine) credentials "
-                                   "- nfs4 mounts with Kerberos will fail\n");
-               }
-       }
-
-       /*
-        * If we don't have any keytab entries we liked, then we have a problem
-        */
-       if (gssd_k5_kt_princ_list == NULL) {
-               retval = ENOENT;
-               goto out;
-       }
-
-       /*
-        * Now go through the list of saved entries and get initial
-        * credentials for them (We can't do this while making the
-        * list because it messes up the keytab iteration cursor
-        * when we use the keytab to get credentials.)
-        */
-       for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
-               if ((gssd_get_single_krb5_cred(context, kt, ple)) == 0) {
-                       gotone++;
-               }
-       }
-       if (!gotone) {
-               printerr(0, "ERROR: No usable machine credentials obtained\n");
-               goto out;
-       }
-
-       retval = 0;
-  out:
-       if (kt) krb5_kt_close(context, kt);
-       krb5_free_context(context);
-
-       return retval;
-}
-
-
 /*
  * Return an array of pointers to names of credential cache files
  * which can be used to try to create gss contexts with a server.
@@ -1218,7 +1017,7 @@ gssd_destroy_krb5_machine_creds(void)
        code = krb5_init_context(&context);
        if (code) {
                printerr(0, "ERROR: %s while initializing krb5\n",
-                        error_message(code));
+                        gssd_k5_err_msg(NULL, code));
                goto out;
        }
 
@@ -1228,14 +1027,14 @@ gssd_destroy_krb5_machine_creds(void)
                if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
                        printerr(0, "WARNING: %s while resolving credential "
                                    "cache '%s' for destruction\n",
-                                error_message(code), ple->ccname);
+                                gssd_k5_err_msg(context, code), ple->ccname);
                        continue;
                }
 
                if ((code = krb5_cc_destroy(context, ccache))) {
                        printerr(0, "WARNING: %s while destroying credential "
                                    "cache '%s'\n",
-                                error_message(code), ple->ccname);
+                                gssd_k5_err_msg(context, code), ple->ccname);
                }
        }
   out:
@@ -1260,14 +1059,15 @@ gssd_refresh_krb5_machine_credential(char *hostname,
        code = krb5_init_context(&context);
        if (code) {
                printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
-                        __FUNCTION__, error_message(code));
+                        __FUNCTION__, gssd_k5_err_msg(NULL, code));
                retval = code;
                goto out;
        }
 
        if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
                printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
-                        __FUNCTION__, error_message(code), keytabfile);
+                        __FUNCTION__, gssd_k5_err_msg(context, code),
+                        keytabfile);
                goto out;
        }
 
@@ -1307,3 +1107,25 @@ out:
        return retval;
 }
 
+/*
+ * A common routine for getting the Kerberos error message
+ */
+const char *
+gssd_k5_err_msg(krb5_context context, krb5_error_code code)
+{
+       const char *msg = NULL;
+#if HAVE_KRB5_GET_ERROR_MESSAGE
+       if (context != NULL)
+               msg = krb5_get_error_message(context, code);
+#endif
+       if (msg != NULL)
+               return msg;
+#if HAVE_KRB5
+       return error_message(code);
+#else
+       if (context != NULL)
+               return krb5_get_err_text(context, code);
+       else
+               return error_message(code);
+#endif
+}