aklog: avoid infinite lifetime tokens by default
[openafs.git] / src / aklog / aklog.c
index 1eaa95e..c16a600 100644 (file)
 #define DIRSTRING "/"          /* String form of above */
 #define VOLMARKER ':'          /* Character separating cellname from mntpt */
 #define VOLMARKERSTRING ":"    /* String form of above */
+#define AKIMP_LIFETIME_MAX 720  /* Max token lifetime for akimpersonate in hours (30 days) */
 
 typedef struct {
     char cell[BUFSIZ];
@@ -145,6 +146,7 @@ static int get_user_realm(krb5_context, char **);
 
 #define TRYAGAIN(x) (x == AKLOG_TRYAGAIN || \
                     x == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN || \
+                    x == KRB5_KT_NOTFOUND || \
                     x == KRB5KRB_ERR_GENERIC)
 
 #if defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
@@ -167,6 +169,11 @@ static int get_user_realm(krb5_context, char **);
 #error "Must have either krb5_princ_size or krb5_principal_get_comp_string"
 #endif
 
+#if defined(HAVE_ENCODE_KRB5_TICKET)
+extern krb5_error_code encode_krb5_ticket (const krb5_ticket *rep,
+                                          krb5_data **code);
+#endif
+
 #if !defined(HAVE_KRB5_ENCRYPT_TKT_PART) && defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_KRB5_C_ENCRYPT)
 extern krb5_error_code encode_krb5_enc_tkt_part (const krb5_enc_tkt_part *rep,
                                                 krb5_data **code);
@@ -297,6 +304,8 @@ static char *client = NULL;     /* client principal for akimpersonate */
 static linked_list zsublist;   /* List of zephyr subscriptions */
 static linked_list hostlist;   /* List of host addresses */
 static linked_list authedcells;        /* List of cells already logged to */
+static int akimp_lifetime = 36000;  /* Lifetime for akimpersonate tokens. Default 10 hrs */ 
+static int akimplifetime_present = 0; /* Whether a lifetime was specified for akimpersonate */
 
 /* A com_error bodge. The idea here is that this routine lets us lookup
  * things in the system com_err, if the AFS one just tells us the error
@@ -317,9 +326,10 @@ redirect_errors(const char *who, afs_int32 code, const char *fmt, va_list ap)
            krb5_svc_get_msg(code,&str);
 #elif defined(HAVE_KRB5_GET_ERROR_MESSAGE)
            krb5_context context;
-           krb5_init_context(&context);
-           str = krb5_get_error_message(context, code);
-           krb5_free_context(context);
+           if (krb5_init_context(&context) == 0) {
+                str = krb5_get_error_message(context, code);
+                krb5_free_context(context);
+            }
 #else
            ; /* IRIX apparently has neither: use the string we have */
 #endif
@@ -382,19 +392,24 @@ get_cellconfig(const char *config, char *cell,
        exit(AKLOG_AFS);
     }
 
-    if (afsconf_GetLocalCell(configdir, *local_cell, MAXCELLCHARS)) {
-       fprintf(stderr, "%s: can't determine local cell.\n", progname);
-       exit(AKLOG_AFS);
+    if (cell != NULL && cell[0] == '\0') {
+       /* Use the local cell */
+       cell = NULL;
     }
 
-    if ((cell == NULL) || (cell[0] == 0))
-       cell = *local_cell;
-
     /* XXX - This function modifies 'cell' by passing it through lcstring */
     if (afsconf_GetCellInfo(configdir, cell, NULL, cellconfig)) {
-       fprintf(stderr, "%s: Can't get information about cell %s.\n",
-               progname, cell);
+       if (cell != NULL) {
+           fprintf(stderr, "%s: Can't get information about cell %s.\n",
+                   progname, cell);
+       } else {
+           fprintf(stderr, "%s: Can't get information about the local cell.\n",
+                   progname);
+       }
        status = AKLOG_AFS;
+    } else if (afsconf_GetLocalCell(configdir, *local_cell, MAXCELLCHARS)) {
+       fprintf(stderr, "%s: can't determine local cell.\n", progname);
+       exit(AKLOG_AFS);
     }
 
     afsconf_Close(configdir);
@@ -853,7 +868,7 @@ rxkad_get_converted_token(krb5_context context, krb5_creds *v5cred,
 static int
 rxkad_get_token(krb5_context context, struct afsconf_cell *cell, char *realm,
                struct ktc_tokenUnion **token, char **authuser, int *foreign) {
-    krb5_creds *v5cred;
+    krb5_creds *v5cred = NULL;
     char *realmUsed = NULL;
     char *username = NULL;
     int status;
@@ -864,7 +879,7 @@ rxkad_get_token(krb5_context context, struct afsconf_cell *cell, char *realm,
 
     status = rxkad_get_ticket(context, realm, cell, &v5cred, &realmUsed);
     if (status)
-       return status;
+       goto out;
 
     if (do524)
        status = rxkad_get_converted_token(context, v5cred, token, &username);
@@ -895,6 +910,8 @@ out:
        free(realmUsed);
     if (username)
        free(username);
+    if (v5cred)
+        krb5_free_creds(context, v5cred);
 
     return status;
 }
@@ -1097,8 +1114,9 @@ auth_to_cell(krb5_context context, const char *config,
         * We don't care about the return value, but need to collect it
         * to avoid compiler warnings.
         */
-       if (write(2,"",0) < 0) /* dummy write */
-           ; /* don't care */
+       if (write(2,"",0) < 0) {
+           /* dummy write, don't care */
+       }
 #endif
        token_setPag(token, afssetpag);
        status = ktc_SetTokenEx(token);
@@ -1421,7 +1439,7 @@ usage(void)
            "[-d] [[-cell | -c] cell [-k krb_realm]] ",
            "[[-p | -path] pathname]\n",
            "    [-zsubs] [-hosts] [-noauth] [-noprdb] [-force] [-setpag] \n"
-               "    [-linked]"
+               "    [-linked] [-insecure_des]"
 #ifndef HAVE_NO_KRB5_524
                " [-524]"
 #endif
@@ -1440,6 +1458,7 @@ usage(void)
 #ifndef HAVE_NO_KRB5_524
     fprintf(stderr, "    -524 means use the 524 converter instead of V5 directly\n");
 #endif
+    fprintf(stderr, "    -insecure_des enables insecure single-DES for krb5.\n");
     fprintf(stderr, "    No commandline arguments means ");
     fprintf(stderr, "authenticate to the local cell.\n");
     fprintf(stderr, "\n");
@@ -1453,6 +1472,7 @@ main(int argc, char *argv[])
     int status = AKLOG_SUCCESS;
     int i;
     int somethingswrong = FALSE;
+    int insecure_des = 0;
 
     cellinfo_t cellinfo;
 
@@ -1532,19 +1552,6 @@ main(int argc, char *argv[])
     initialize_PT_error_table();
     afs_set_com_err_hook(redirect_errors);
 
-    /*
-     * Enable DES enctypes, which are currently still required for AFS.
-     * krb5_allow_weak_crypto is MIT Kerberos 1.8.  krb5_enctype_enable is
-     * Heimdal.
-     */
-#if defined(HAVE_KRB5_ENCTYPE_ENABLE)
-    i = krb5_enctype_valid(context, ETYPE_DES_CBC_CRC);
-    if (i)
-        krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
-#elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
-    krb5_allow_weak_crypto(context, 1);
-#endif
-
     /* Initialize list of cells to which we have authenticated */
     ll_init(&authedcells);
 
@@ -1584,6 +1591,28 @@ main(int argc, char *argv[])
            }
            else
                usage();
+        else if ((strcmp(argv[i], "-token-lifetime") == 0))
+           if (++i < argc) {
+               status = util_GetInt32(argv[i], &akimp_lifetime);
+               if (status) {
+                   fprintf(stderr,
+                           "%s: invalid value specified for token-lifetime.\n",
+                           progname);
+                   exit(AKLOG_MISC);
+               }
+
+               if (akimp_lifetime < 0 || akimp_lifetime > AKIMP_LIFETIME_MAX) {
+                   fprintf(stderr,
+                            "%s: token-lifetime must be within 0 and %d hrs.\n",
+                           progname, AKIMP_LIFETIME_MAX);
+                   exit(AKLOG_MISC);
+               }
+
+               akimp_lifetime = akimp_lifetime * 60 * 60;
+               akimplifetime_present = TRUE;
+           }
+           else
+               usage();
        else if ((strcmp(argv[i], "-principal") == 0))
            if (++i < argc) {
                client = argv[i];
@@ -1604,6 +1633,8 @@ main(int argc, char *argv[])
            }
            else
                usage();
+       else if (strcmp(argv[i], "-insecure_des") == 0)
+           insecure_des = 1;
        else if (argv[i][0] == '-')
            usage();
        else if (!pmode && !cmode) {
@@ -1620,6 +1651,26 @@ main(int argc, char *argv[])
        else
            usage();
 
+    /*
+     * Enable DES enctypes if requested.  This is not required when rxkad-k5
+     * is used, but some sites may not have updated.
+     * krb5_allow_weak_crypto is MIT Kerberos 1.8.  krb5_enctype_enable is
+     * Heimdal.
+     */
+    if (insecure_des) {
+#if defined(HAVE_KRB5_ENCTYPE_ENABLE)
+       i = krb5_enctype_valid(context, ETYPE_DES_CBC_CRC);
+       if (i)
+           krb5_enctype_enable(context, ETYPE_DES_CBC_CRC);
+#elif defined(HAVE_KRB5_ALLOW_WEAK_CRYPTO)
+       krb5_allow_weak_crypto(context, 1);
+#else
+       fprintf(stderr,
+           "%s: -insecure_des is not supported by this libkrb5\n", progname);
+       exit(AKLOG_MISC);
+#endif
+    }
+
        if (cmode) {
            if (((i + 1) < argc) && (strcmp(argv[i + 1], "-k") == 0)) {
                i+=2;
@@ -1673,6 +1724,13 @@ main(int argc, char *argv[])
        }
     }
 
+    if (akimplifetime_present && !keytab) {
+       fprintf(stderr,
+               "%s: -token-lifetime is valid only if -keytab is specified.\n",
+               progname);
+       exit(AKLOG_MISC);
+    }
+
     /* If nothing was given, log to the local cell. */
     if ((cells.nelements + paths.nelements) == 0) {
        struct passwd *pwd;
@@ -1806,14 +1864,12 @@ get_credv5_akimpersonate(krb5_context context,
                         krb5_principal client_principal,
                         time_t starttime,
                         time_t endtime,
-                        int *allowed_enctypes,
                         int *paddress,
                         krb5_creds** out_creds /* out */ )
 {
-#if defined(USING_HEIMDAL) || (defined(HAVE_ENCODE_KRB5_ENC_TKT) && defined(HAVE_ENCODE_KRB5_TICKET) && defined(HAVE_KRB5_C_ENCRYPT))
+#if defined(USING_HEIMDAL) || (defined(HAVE_ENCODE_KRB5_ENC_TKT_PART) && defined(HAVE_ENCODE_KRB5_TICKET) && defined(HAVE_KRB5_C_ENCRYPT))
     krb5_error_code code;
     krb5_keytab kt = 0;
-    krb5_kt_cursor cursor[1];
     krb5_keytab_entry entry[1];
     krb5_ccache cc = 0;
     krb5_creds *creds = 0;
@@ -1834,14 +1890,11 @@ get_credv5_akimpersonate(krb5_context context,
     krb5_data * temp;
 #endif
     int i;
-    static int any_enctype[] = {0};
     *out_creds = 0;
     if (!(creds = malloc(sizeof *creds))) {
         code = ENOMEM;
         goto cleanup;
     }
-    if (!allowed_enctypes)
-        allowed_enctypes = any_enctype;
 
     cc = 0;
     enctype = 0; /* AKIMPERSONATE_IGNORE_ENCTYPE */
@@ -1860,54 +1913,17 @@ get_credv5_akimpersonate(krb5_context context,
         goto cleanup;
     }
 
-    if (service_principal) {
-        for (i = 0; (enctype = allowed_enctypes[i]) || !i; ++i) {
-           code = krb5_kt_get_entry(context,
-                                    kt,
-                                    service_principal,
-                                    kvno,
-                                    enctype,
-                                    entry);
-           if (!code) {
-               if (allowed_enctypes[i])
-                   deref_keyblock_enctype(session_key) = allowed_enctypes[i];
-               break;
-           }
-        }
-        if (code) {
-           afs_com_err(progname, code,"while scanning keytab entries");
-           goto cleanup;
-        }
-    } else {
-        krb5_keytab_entry new[1];
-        int best = -1;
-        memset(new, 0, sizeof *new);
-        if ((code = krb5_kt_start_seq_get(context, kt, cursor))) {
-            afs_com_err(progname, code, "while starting keytab scan");
-            goto cleanup;
-        }
-        while (!(code = krb5_kt_next_entry(context, kt, new, cursor))) {
-            for (i = 0;
-                    allowed_enctypes[i] && allowed_enctypes[i]
-                    != deref_entry_enctype(new); ++i)
-                ;
-            if ((!i || allowed_enctypes[i]) &&
-               (best < 0 || best > i)) {
-                krb5_free_keytab_entry_contents(context, entry);
-                *entry = *new;
-                memset(new, 0, sizeof *new);
-            } else krb5_free_keytab_entry_contents(context, new);
-        }
-        if ((i = krb5_kt_end_seq_get(context, kt, cursor))) {
-            afs_com_err(progname, i, "while ending keytab scan");
-            code = i;
-            goto cleanup;
-        }
-        if (best < 0) {
-            afs_com_err(progname, code, "while scanning keytab");
-            goto cleanup;
-        }
+    code = krb5_kt_get_entry(context,
+                            kt,
+                            service_principal,
+                            kvno,
+                            enctype,
+                            entry);
+    if (!code)
         deref_keyblock_enctype(session_key) = deref_entry_enctype(entry);
+    else {
+        afs_com_err(progname, code, "while scanning keytab entries");
+        goto cleanup;
     }
 
     /* Make Ticket */
@@ -1930,10 +1946,6 @@ get_credv5_akimpersonate(krb5_context context,
     enc_tkt_reply->authtime = starttime;
     enc_tkt_reply->starttime = temp_time;
     *enc_tkt_reply->starttime = starttime;
-#if 0
-    enc_tkt_reply->renew_till = temp_time + 1;
-    *enc_tkt_reply->renew_till = endtime;
-#endif
     enc_tkt_reply->endtime = endtime;
 #else
     if ((code = krb5_c_make_random_key(context,
@@ -2103,10 +2115,6 @@ cleanup:
     if (deref_enc_data(&ticket_reply->enc_part))
         free(deref_enc_data(&ticket_reply->enc_part));
     krb5_free_keytab_entry_contents(context, entry);
-    if (client_principal)
-        krb5_free_principal(context, client_principal);
-    if (service_principal)
-        krb5_free_principal(context, service_principal);
     if (cc)
         krb5_cc_close(context, cc);
     if (kt)
@@ -2126,7 +2134,6 @@ get_credv5(krb5_context context, char *name, char *inst, char *realm,
 {
     krb5_creds increds;
     krb5_error_code r;
-    static krb5_principal client_principal = 0;
 
     afs_dprintf("Getting tickets: %s%s%s@%s\n", name,
            (inst && inst[0]) ? "/" : "", inst ? inst : "", realm);
@@ -2138,50 +2145,63 @@ get_credv5(krb5_context context, char *name, char *inst, char *realm,
                                  name,
                                  (inst && strlen(inst)) ? inst : NULL,
                                  NULL))) {
-        return r;
+        goto out;
     }
 
 
     if (!_krb425_ccache) {
         r = krb5_cc_default(context, &_krb425_ccache);
        if (r)
-           return r;
+           goto out;
     }
-    if (!client_principal) {
-       if (client) {
-           r = krb5_parse_name(context, client,  &client_principal);
-       } else {
-           r = krb5_cc_get_principal(context, _krb425_ccache, &client_principal);
-       }
-       if (r)
-           return r;
+
+    if (client) {
+        r = krb5_parse_name(context, client,  &increds.client);
+    } else {
+        r = krb5_cc_get_principal(context, _krb425_ccache, &increds.client);
     }
 
-    increds.client = client_principal;
+    if (r)
+       goto out;
+
     increds.times.endtime = 0;
     if (do524)
        /* Ask for DES since that is what V4 understands */
        get_creds_enctype((&increds)) = ENCTYPE_DES_CBC_CRC;
 
     if (keytab) {
-       int allowed_enctypes[] = {
-           ENCTYPE_DES_CBC_CRC, 0
-       };
+       afs_int32 start, end;
+       start = time(NULL);
+
+       if (akimp_lifetime == 0) {
+           end = MAX_AFS_INT32;
+       } else {
+           end = start + akimp_lifetime;
+       }
 
        r = get_credv5_akimpersonate(context,
                                     keytab,
                                     increds.server,
                                     increds.client,
-                                    300, ((~0U)>>1),
-                                    allowed_enctypes,
+                                    start, end,
                                     0 /* paddress */,
                                     creds /* out */);
     } else {
        r = krb5_get_credentials(context, 0, _krb425_ccache, &increds, creds);
     }
-    return r;
-}
 
+ out:
+
+   if (increds.server) {
+       krb5_free_principal(context, increds.server);
+   }
+
+   if (increds.client) {
+       krb5_free_principal(context, increds.client);
+   }
+
+   return r;
+}
 
 static int
 get_user_realm(krb5_context context, char **realm)