cifscreds: fix up some whitespace, typos and build warnings in pam_cifscreds.c
[jlayton/cifs-utils.git] / cifs.upcall.c
index 9e7f7e201339be253507122995ccc86b61a4f1b5..cc65824d3689f53476d27f66bdd0d24e956a5c40 100644 (file)
@@ -53,7 +53,8 @@
 #include "cifs_spnego.h"
 
 #define        CIFS_DEFAULT_KRB5_DIR           "/tmp"
-#define        CIFS_DEFAULT_KRB5_PREFIX        "krb5cc_"
+#define        CIFS_DEFAULT_KRB5_USER_DIR      "/run/user/%U"
+#define        CIFS_DEFAULT_KRB5_PREFIX        "krb5cc"
 #define CIFS_DEFAULT_KRB5_KEYTAB       "/etc/krb5.keytab"
 
 #define        MAX_CCNAME_LEN                  PATH_MAX + 5
@@ -180,10 +181,9 @@ err_cache:
 
 static int krb5cc_filter(const struct dirent *dirent)
 {
-       if (strstr(dirent->d_name, CIFS_DEFAULT_KRB5_PREFIX))
-               return 1;
-       else
-               return 0;
+       /* subtract 1 for the null terminator */
+       return !strncmp(dirent->d_name, CIFS_DEFAULT_KRB5_PREFIX,
+                       sizeof(CIFS_DEFAULT_KRB5_PREFIX) - 1);
 }
 
 static char *
@@ -244,7 +244,7 @@ init_cc_from_keytab(const char *keytab_name, const char *user)
        if (ccname == NULL)
                syslog(LOG_ERR, "Unable to allocate memory");
 icfk_cleanup:
-       my_creds.client = 0;
+       my_creds.client = (krb5_principal)0;
        krb5_free_cred_contents(context, &my_creds);
 
        if (me)
@@ -258,14 +258,47 @@ icfk_cleanup:
        return ccname;
 }
 
+/* resolve a pattern to an actual directory path */
+static char *resolve_krb5_dir(const char *pattern, uid_t uid)
+{
+       char name[MAX_CCNAME_LEN];
+       int i;
+       size_t j;
+       for (i = 0, j = 0; (pattern[i] != '\0') && (j < sizeof(name)); i++) {
+               switch (pattern[i]) {
+               case '%':
+                       switch (pattern[i + 1]) {
+                       case '%':
+                               name[j++] = pattern[i];
+                               i++;
+                               break;
+                       case 'U':
+                               j += snprintf(name + j, sizeof(name) - j,
+                                             "%lu", (unsigned long) uid);
+                               i++;
+                               break;
+                       }
+                       break;
+               default:
+                       name[j++] = pattern[i];
+                       break;
+               }
+       }
+       if ((j > 0) && (j < sizeof(name)))
+               return strndup(name, MAX_CCNAME_LEN);
+       else
+               return NULL;
+}
+
 /* search for a credcache that looks like a likely candidate */
-static char *find_krb5_cc(const char *dirname, uid_t uid)
+static char *find_krb5_cc(const char *dirname, uid_t uid,
+                         char **best_cache, time_t *best_time)
 {
        struct dirent **namelist;
        struct stat sbuf;
-       char ccname[MAX_CCNAME_LEN], *credpath, *best_cache = NULL;
+       char ccname[MAX_CCNAME_LEN], *credpath;
        int i, n;
-       time_t cred_time, best_time = 0;
+       time_t cred_time;
 
        n = scandir(dirname, &namelist, krb5cc_filter, NULL);
        if (n < 0) {
@@ -292,6 +325,11 @@ static char *find_krb5_cc(const char *dirname, uid_t uid)
                        free(namelist[i]);
                        continue;
                }
+               if (S_ISDIR(sbuf.st_mode)) {
+                       snprintf(ccname, sizeof(ccname), "DIR:%s/%s", dirname,
+                                namelist[i]->d_name);
+                       credpath = ccname + 4;
+               } else
                if (!S_ISREG(sbuf.st_mode)) {
                        syslog(LOG_DEBUG, "%s: %s is not a regular file",
                               __func__, credpath);
@@ -305,7 +343,7 @@ static char *find_krb5_cc(const char *dirname, uid_t uid)
                        continue;
                }
 
-               if (cred_time <= best_time) {
+               if (cred_time <= *best_time) {
                        syslog(LOG_DEBUG, "%s: %s expires sooner than current "
                               "best.", __func__, ccname);
                        free(namelist[i]);
@@ -313,14 +351,14 @@ static char *find_krb5_cc(const char *dirname, uid_t uid)
                }
 
                syslog(LOG_DEBUG, "%s: %s is valid ccache", __func__, ccname);
-               free(best_cache);
-               best_cache = strndup(ccname, MAX_CCNAME_LEN);
-               best_time = cred_time;
+               free(*best_cache);
+               *best_cache = strndup(ccname, MAX_CCNAME_LEN);
+               *best_time = cred_time;
                free(namelist[i]);
        }
        free(namelist);
 
-       return best_cache;
+       return *best_cache;
 }
 
 static int
@@ -344,11 +382,20 @@ cifs_krb5_get_req(const char *host, const char *ccname,
                return ret;
        }
 
-       ret = krb5_cc_resolve(context, ccname, &ccache);
-       if (ret) {
-               syslog(LOG_DEBUG, "%s: unable to resolve %s to ccache\n",
-                      __func__, ccname);
-               goto out_free_context;
+       if (ccname) {
+               ret = krb5_cc_resolve(context, ccname, &ccache);
+               if (ret) {
+                       syslog(LOG_DEBUG, "%s: unable to resolve %s to ccache\n",
+                              __func__, ccname);
+                       goto out_free_context;
+               }
+       } else {
+               ret = krb5_cc_default(context, &ccache);
+               if (ret) {
+                       syslog(LOG_DEBUG, "%s: krb5_cc_default: %d",
+                               __func__, (int)ret);
+                       goto out_free_context;
+               }
        }
 
        memset(&in_creds, 0, sizeof(in_creds));
@@ -415,6 +462,14 @@ cifs_krb5_get_req(const char *host, const char *ccname,
         */
        in_data.data = discard_const_p(char, gss_cksum);
        in_data.length = 24;
+
+       /* MIT krb5 < 1.7 is missing the prototype, but still has the symbol */
+#if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
+       krb5_error_code krb5_auth_con_set_req_cksumtype(
+               krb5_context      context,
+               krb5_auth_context auth_context,
+               krb5_cksumtype    cksumtype);
+#endif
        ret = krb5_auth_con_set_req_cksumtype(context, auth_context, 0x8003);
        if (ret) {
                syslog(LOG_DEBUG, "%s: unable to set 0x8003 checksum",
@@ -759,13 +814,14 @@ lowercase_string(char *c)
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: %s [-k /path/to/krb5.conf] [-t] [-v] [-l] key_serial\n", prog);
+       fprintf(stderr, "Usage: %s [ -K /path/to/keytab] [-k /path/to/krb5.conf] [-t] [-v] [-l] key_serial\n", prog);
 }
 
-const struct option long_options[] = {
+static const struct option long_options[] = {
        {"krb5conf", 1, NULL, 'k'},
        {"legacy-uid", 0, NULL, 'l'},
        {"trust-dns", 0, NULL, 't'},
+       {"keytab", 1, NULL, 'K'},
        {"version", 0, NULL, 'v'},
        {NULL, 0, NULL, 0}
 };
@@ -780,19 +836,20 @@ int main(const int argc, char *const argv[])
        unsigned int have;
        long rc = 1;
        int c, try_dns = 0, legacy_uid = 0;
-       char *buf, *ccname = NULL;
+       char *buf, *ccdir = NULL, *ccname = NULL, *best_cache = NULL;
        char hostbuf[NI_MAXHOST], *host;
        struct decoded_args arg;
        const char *oid;
        uid_t uid;
        char *keytab_name = CIFS_DEFAULT_KRB5_KEYTAB;
+       time_t best_time = 0;
 
        hostbuf[0] = '\0';
        memset(&arg, 0, sizeof(arg));
 
        openlog(prog, 0, LOG_DAEMON);
 
-       while ((c = getopt_long(argc, argv, "ck:ltv", long_options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "ck:K:ltv", long_options, NULL)) != -1) {
                switch (c) {
                case 'c':
                        /* legacy option -- skip it */
@@ -806,10 +863,14 @@ int main(const int argc, char *const argv[])
                                goto out;
                        }
                        break;
+               case 'K':
+                       keytab_name = optarg;
+                       break;
                case 'l':
                        legacy_uid++;
                        break;
                case 'v':
+                       rc = 0;
                        printf("version: %s\n", VERSION);
                        goto out;
                default:
@@ -888,7 +949,12 @@ int main(const int argc, char *const argv[])
                syslog(LOG_ERR, "setuid: %s", strerror(errno));
                goto out;
        }
-       ccname = find_krb5_cc(CIFS_DEFAULT_KRB5_DIR, uid);
+       ccdir = resolve_krb5_dir(CIFS_DEFAULT_KRB5_USER_DIR, uid);
+       if (ccdir != NULL)
+               find_krb5_cc(ccdir, uid, &best_cache, &best_time);
+       ccname = find_krb5_cc(CIFS_DEFAULT_KRB5_DIR, uid, &best_cache,
+                             &best_time);
+       SAFE_FREE(ccdir);
 
        /* Couldn't find credcache? Try to use keytab */
        if (ccname == NULL && arg.username != NULL)
@@ -986,10 +1052,12 @@ retry_new_hostname:
                break;
        }
 
-       if (rc)
+       if (rc) {
+               syslog(LOG_DEBUG, "Unable to obtain service ticket");
                goto out;
+       }
 
-       /* pack SecurityBLob and SessionKey into downcall packet */
+       /* pack SecurityBlob and SessionKey into downcall packet */
        datalen =
            sizeof(struct cifs_spnego_msg) + secblob.length + sess_key.length;
        keydata = (struct cifs_spnego_msg *)calloc(sizeof(char), datalen);
@@ -1021,8 +1089,10 @@ out:
         * make sure the kernel doesn't hang it off of a searchable keyring
         * and interfere with the next attempt to instantiate the key.
         */
-       if (rc != 0 && key == 0)
+       if (rc != 0 && key == 0) {
+               syslog(LOG_DEBUG, "Negating key");
                keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
+       }
        data_blob_free(&secblob);
        data_blob_free(&sess_key);
        SAFE_FREE(ccname);
@@ -1030,5 +1100,6 @@ out:
        SAFE_FREE(arg.ip);
        SAFE_FREE(arg.username);
        SAFE_FREE(keydata);
+       syslog(LOG_DEBUG, "Exit status %ld", rc);
        return rc;
 }