]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/gssd/krb5_util.c
gssd_setup_krb5_user_gss_ccache must return an error if no usable cache is
[nfs-utils.git] / utils / gssd / krb5_util.c
index 50773b1cdd1c215d5ec32c9b58d9267e578c9741..4a4d10badb58e751b9db98049ed50981d3a0976e 100644 (file)
@@ -131,7 +131,8 @@ struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
 /*==========================*/
 
 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_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);
 
@@ -159,7 +160,7 @@ select_krb5_ccache(const struct dirent *d)
 }
 
 /*
- * Look in the ccachedir for files that look like they
+ * Look in directory "dirname" for files that look like they
  * are Kerberos Credential Cache files for a given UID.  Return
  * non-zero and the dirent pointer for the entry most likely to be
  * what we want. Otherwise, return zero and no dirent pointer.
@@ -170,7 +171,7 @@ select_krb5_ccache(const struct dirent *d)
  *     1 => found an existing entry
  */
 static int
-gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
+gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d)
 {
        struct dirent **namelist;
        int n;
@@ -181,9 +182,10 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
 
        memset(&best_match_stat, 0, sizeof(best_match_stat));
        *d = NULL;
-       n = scandir(ccachedir, &namelist, select_krb5_ccache, 0);
+       n = scandir(dirname, &namelist, select_krb5_ccache, 0);
        if (n < 0) {
-               perror("scandir looking for krb5 credentials caches");
+               printerr(1, "Error doing scandir on directory '%s': %s\n",
+                       dirname, strerror(errno));
        }
        else if (n > 0) {
                char statname[1024];
@@ -191,7 +193,7 @@ gssd_find_existing_krb5_ccache(uid_t uid, struct dirent **d)
                        printerr(3, "CC file '%s' being considered\n",
                                 namelist[i]->d_name);
                        snprintf(statname, sizeof(statname),
-                                "%s/%s", ccachedir, namelist[i]->d_name);
+                                "%s/%s", dirname, namelist[i]->d_name);
                        if (lstat(statname, &tmp_stat)) {
                                printerr(0, "Error doing stat on file '%s'\n",
                                         statname);
@@ -277,7 +279,9 @@ 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 };
+       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 */
@@ -289,8 +293,9 @@ limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid)
                                    &credh, NULL, NULL);
 
        if (maj_stat != GSS_S_COMPLETE) {
-               pgsserr("gss_acquire_cred",
-                       maj_stat, min_stat, &krb5oid);
+               if (get_verbosity() > 0)
+                       pgsserr("gss_acquire_cred",
+                               maj_stat, min_stat, &krb5oid);
                return -1;
        }
 
@@ -323,7 +328,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];
@@ -351,19 +361,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;
        }
@@ -378,7 +409,7 @@ gssd_get_single_krb5_cred(krb5_context context,
            cache_type = "FILE";
        snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s",
                cache_type,
-               GSSD_DEFAULT_CRED_DIR, GSSD_DEFAULT_CRED_PREFIX,
+               ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
                GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
        ple->endtime = my_creds.times.endtime;
        if (ple->ccname != NULL)
@@ -392,17 +423,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;
        }
 
@@ -410,6 +442,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)
@@ -652,14 +688,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;
        }
@@ -669,7 +705,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;
                }
@@ -705,7 +741,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;
@@ -743,7 +779,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));
@@ -754,7 +790,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;
        }
 
@@ -767,7 +803,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;
        }
@@ -799,7 +835,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;
                        }
@@ -807,7 +844,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 "
@@ -856,10 +894,11 @@ out:
  * do the best we can.
  *
  * Returns:
- *     void
+ *     0 => a ccache was found
+ *     1 => no ccache was found
  */
-void
-gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
+int
+gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirname)
 {
        char                    buf[MAX_NETOBJ_SZ];
        struct dirent           *d;
@@ -867,17 +906,16 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername)
        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, &d)) {
-               snprintf(buf, sizeof(buf), "FILE:%s/%s",
-                       ccachedir, d->d_name);
+       if (gssd_find_existing_krb5_ccache(uid, dirname, &d)) {
+               snprintf(buf, sizeof(buf), "FILE:%s/%s", dirname, d->d_name);
                free(d);
        }
        else
-               snprintf(buf, sizeof(buf), "FILE:%s/%s%u",
-                       ccachedir, GSSD_DEFAULT_CRED_PREFIX, uid);
+               return 1;
        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;
 }
 
 /*
@@ -984,7 +1022,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;
        }
 
@@ -994,14 +1032,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:
@@ -1026,14 +1064,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;
        }
 
@@ -1073,3 +1112,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
+}