NIM file ccache support improvements
authorJeffrey Altman <jaltman@secure-endpoints.com>
Wed, 29 Aug 2007 22:38:26 +0000 (22:38 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Wed, 29 Aug 2007 22:38:26 +0000 (22:38 +0000)
NIM supports the ability of the user to specify an
explicit ccache name for use with an identity.  If
this ccache is a FILE ccache, we need to be able to
store credentials into the ccache.  krb5cred.dll
did not previously specify the KRB5_TC_OPENCLOSE flag
on the ccache when setting other flags such as
KRB5_TC_NOTICKET (which is used with MSLSA ccaches).
As a result, open/close mode was turned off, the
ccache file would be opened in read-only mode and
attempts to store credentials into the ccache would
fail.  This is fixed by specifying KRB5_TC_OPENCLOSE
when setting the ccache flags.

When a CCAPI implementation is unavailable, we need
to automatically generate the FILE ccache name if
one has not already been specified.  We default to
a file stored in the user's Local Settings\Temp
directory.  The generated ccache is then added to
the file ccache watch list.

Finally, some users have complained about the
behavior of Microsoft Vista's UAC mode and how
it makes the CCAPI cache useless for storing
credentials that must be used in conjunction
with processes that do not have restricted
privileges since those processes run in a
separate logon session.  For these users we
have added a "DefaultToFileCache" registry
value that can be specified to force the use
of FILE ccaches in preference to CCAPI ccaches
when there is no explicit ccache specified
for a given identity.  Unlike CCAPI ccaches,
the FILE ccaches are accessible from both
restricted and unrestricted processes when
UAC is active.

ticket: new
component: windows

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@19897 dc483132-0cff-0310-8789-dd5450dbe970

src/windows/identity/plugins/krb5/krb5configid.c
src/windows/identity/plugins/krb5/krb5funcs.c
src/windows/identity/plugins/krb5/krb5newcreds.c
src/windows/identity/plugins/krb5/krbconfig.csv

index a2688e1e7d1abf408040b5afd3a379b4d6dd2945..2f3fe62c84ba77bc084fe32e3ccabddcc3bb8683 100644 (file)
@@ -61,8 +61,7 @@ k5_id_read_params(k5_id_dlg_data * d) {
     khm_size cb;
     khm_int32 rv;
     khm_int32 t;
-    khm_handle csp_ident;
-    khm_handle csp_idroot = NULL;
+    khm_handle csp_ident = NULL;
 
     cb = sizeof(idname);
     rv = khui_cfg_get_name(d->cfg.ctx_node, idname, &cb);
@@ -75,17 +74,7 @@ k5_id_read_params(k5_id_dlg_data * d) {
     assert(KHM_SUCCEEDED(rv));
 #endif
 
-    rv = kcdb_identity_get_config(d->ident, 0, &csp_idroot);
-    if (KHM_SUCCEEDED(rv) &&
-        KHM_SUCCEEDED(khc_open_space(csp_idroot, CSNAME_KRB5CRED, 0,
-                                     &csp_ident))) {
-        khc_shadow_space(csp_ident, csp_params);
-    } else {
-        csp_ident = csp_params;
-    }
-
-    if (csp_idroot)
-        khc_close_space(csp_idroot);
+    khm_krb5_get_identity_config(d->ident, 0, &csp_ident);
 
     rv = khc_read_int32(csp_ident, L"DefaultLifetime",  &t);
     if (KHM_SUCCEEDED(rv))
@@ -146,7 +135,7 @@ k5_id_read_params(k5_id_dlg_data * d) {
     d->tc_renew.min = 0;
     d->tc_renew.max = 3600 * 24 * 30;
 
-    if (csp_ident != csp_params)
+    if (csp_ident)
         khc_close_space(csp_ident);
 }
 
index 4dc854845c1f1e67614de3d6409169b0d71698ee..9c8db75098944b7ed9a054b48b5ef2938ed86abf 100644 (file)
@@ -515,8 +515,6 @@ static long get_tickets_from_cache(krb5_context ctx,
 
 #ifdef KRB5_TC_NOTICKET
     flags = KRB5_TC_NOTICKET;
-#else
-    flags = 0;
 #endif
 
     {
@@ -1005,6 +1003,7 @@ khm_krb5_renew_cred(khm_handle cred)
     khm_boolean                istgt = FALSE;
 
     khm_int32           flags;
+    int                 ccflags = 0;
 
     cbname = sizeof(wname);
     kcdb_cred_get_name(cred, wname, &cbname);
@@ -1055,9 +1054,8 @@ khm_krb5_renew_cred(khm_handle cred)
     in_creds.client = me;
     in_creds.server = server;
 
-#ifdef KRB5_TC_NOTICKET
-    pkrb5_cc_set_flags(ctx, cc, 0);
-#endif
+    ccflags = KRB5_TC_OPENCLOSE;
+    pkrb5_cc_set_flags(ctx, cc, ccflags);
 
     if (strlen("krbtgt") != krb5_princ_name(ctx, server)->length ||
         strncmp("krbtgt", krb5_princ_name(ctx, server)->data, krb5_princ_name(ctx, server)->length)) 
@@ -1080,9 +1078,6 @@ khm_krb5_renew_cred(khm_handle cred)
        code = pkrb5_get_renewed_creds(ctx, &cc_creds, me, cc, NULL);
     }
 
-#ifdef KRB5_TC_NOTICKET
-    pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);
-#endif
     if (code) {
        if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||
             code != KRB5_KDC_UNREACH)
@@ -1094,6 +1089,8 @@ khm_krb5_renew_cred(khm_handle cred)
        code = pkrb5_cc_initialize(ctx, cc, me);
        if (code) goto cleanup;
 
+        ccflags = KRB5_TC_OPENCLOSE;
+        pkrb5_cc_set_flags(ctx, cc, ccflags);
     }
 
     code = pkrb5_cc_store_cred(ctx, cc, istgt ? &cc_creds : out_creds);
@@ -1101,7 +1098,6 @@ khm_krb5_renew_cred(khm_handle cred)
 
 
  cleanup:
-
     if (in_creds.client == me)
         in_creds.client = NULL;
     if (in_creds.server == server)
@@ -1147,6 +1143,7 @@ khm_krb5_renew_ident(khm_handle identity)
     wchar_t             idname[KCDB_IDENT_MAXCCH_NAME];
     khm_size            cb;
     khm_int32           k5_flags;
+    int                 ccflags;
 
     memset(&my_creds, 0, sizeof(krb5_creds));
 
@@ -1304,13 +1301,13 @@ khm_krb5_renew_ident(khm_handle identity)
     my_creds.client = me;
     my_creds.server = server;
 
-#ifdef KRB5_TC_NOTICKET
-    pkrb5_cc_set_flags(ctx, cc, 0);
-#endif
+    pkrb5_cc_set_flags(ctx, cc, KRB5_TC_OPENCLOSE);
     code = pkrb5_get_renewed_creds(ctx, &my_creds, me, cc, NULL);
+    ccflags = KRB5_TC_OPENCLOSE;
 #ifdef KRB5_TC_NOTICKET
-    pkrb5_cc_set_flags(ctx, cc, KRB5_TC_NOTICKET);
+    ccflags |= KRB5_TC_NOTICKET;
 #endif
+    pkrb5_cc_set_flags(ctx, cc, ccflags);
     if (code) {
         if ( code != KRB5KDC_ERR_ETYPE_NOSUPP ||
             code != KRB5_KDC_UNREACH)
@@ -1321,6 +1318,9 @@ khm_krb5_renew_ident(khm_handle identity)
     code = pkrb5_cc_initialize(ctx, cc, me);
     if (code) goto cleanup;
 
+    code = pkrb5_cc_set_flags(ctx, cc, KRB5_TC_OPENCLOSE);
+    if (code) goto cleanup;
+
     code = pkrb5_cc_store_cred(ctx, cc, &my_creds);
     if (code) goto cleanup;
 
@@ -3057,31 +3057,130 @@ get_libdefault_string(profile_t profile, const char * realm,
     return code;
 }
 
+
+const struct escape_char_sequences {
+    wchar_t character;
+    wchar_t escape;
+} file_cc_escapes[] = {
+
+    /* in ASCII order */
+
+    {L'\"', L'd'},
+    {L'$',  L'$'},
+    {L'%',  L'r'},
+    {L'\'', L'i'},
+    {L'*',  L's'},
+    {L'/',  L'f'},
+    {L':',  L'c'},
+    {L'<',  L'l'},
+    {L'>',  L'g'},
+    {L'?',  L'q'},
+    {L'\\', L'b'},
+    {L'|',  L'p'}
+};
+
+static void
+escape_string_for_filename(const wchar_t * s,
+                           wchar_t * buf,
+                           khm_size cb_buf)
+{
+    wchar_t * d;
+    int i;
+
+    for (d = buf; *s && cb_buf > sizeof(wchar_t) * 3; s++) {
+        if (iswpunct(*s)) {
+            for (i=0; i < ARRAYLENGTH(file_cc_escapes); i++) {
+                if (*s == file_cc_escapes[i].character)
+                    break;
+            }
+
+            if (i < ARRAYLENGTH(file_cc_escapes)) {
+                *d++ = L'$';
+                *d++ = file_cc_escapes[i].escape;
+                cb_buf -= sizeof(wchar_t) * 2;
+                continue;
+            }
+        }
+
+        *d++ = *s;
+        cb_buf -= sizeof(wchar_t);
+    }
+
+#ifdef DEBUG
+    assert(cb_buf >= sizeof(wchar_t));
+#endif
+    *d++ = L'\0';
+}
+
+static khm_int32
+get_default_file_cache_for_identity(const wchar_t * idname,
+                                    wchar_t * ccname,
+                                    khm_size * pcb)
+{
+    wchar_t escf[MAX_PATH] = L"";
+    wchar_t tmppath[MAX_PATH] = L"";
+    wchar_t tccname[MAX_PATH];
+    khm_size cb;
+
+    escape_string_for_filename(idname, escf, sizeof(escf));
+    GetTempPath(ARRAYLENGTH(tmppath), tmppath);
+
+    StringCbPrintf(tccname, sizeof(tccname), L"FILE:%s\\krb5cc.%s", tmppath, escf);
+    StringCbLength(tccname, sizeof(tccname), &cb);
+    cb += sizeof(wchar_t);
+
+    if (ccname && *pcb >= cb) {
+        StringCbCopy(ccname, *pcb, tccname);
+        *pcb = cb;
+        return KHM_ERROR_SUCCESS;
+    } else {
+        *pcb = cb;
+        return KHM_ERROR_TOO_LONG;
+    }
+}
+
 khm_int32
 khm_krb5_get_identity_default_ccache(khm_handle ident, wchar_t * buf, khm_size * pcb) {
     khm_handle csp_id = NULL;
     khm_int32 rv = KHM_ERROR_SUCCESS;
+    khm_size cbt;
 
     rv = khm_krb5_get_identity_config(ident, 0, &csp_id);
 
+    cbt = *pcb;
     if (KHM_SUCCEEDED(rv))
-        rv = khc_read_string(csp_id, L"DefaultCCName", buf, pcb);
+        rv = khc_read_string(csp_id, L"DefaultCCName", buf, &cbt);
 
-    if (KHM_FAILED(rv) && rv != KHM_ERROR_TOO_LONG) {
+    if ((KHM_FAILED(rv) && rv != KHM_ERROR_TOO_LONG) ||
+        (KHM_SUCCEEDED(rv) && buf[0] == L'\0')) {
         /* we need to figure out the default ccache from the principal
            name */
         wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
         wchar_t ccname[MAX_PATH];
         khm_size cb;
+        khm_int32 use_file_cache = 0;
+
+        khc_read_int32(csp_id, L"DefaultToFileCache", &use_file_cache);
 
         cb = sizeof(idname);
         kcdb_identity_get_name(ident, idname, &cb);
-        StringCbCopy(ccname, sizeof(ccname), idname);
+
+        if (use_file_cache) {
+            cb = sizeof(ccname);
+            rv = get_default_file_cache_for_identity(idname, ccname, &cb);
+#ifdef DEBUG
+            assert(KHM_SUCCEEDED(rv));
+#endif
+        } else {                /* generate an API: cache */
+            StringCbPrintf(ccname, sizeof(ccname), L"API:%s", idname);
+        }
         khm_krb5_canon_cc_name(ccname, sizeof(ccname));
-        StringCbLength(ccname, sizeof(ccname), &cb);
 
         _reportf(L"Setting CCache [%s] for identity [%s]", ccname, idname);
 
+        StringCbLength(ccname, sizeof(ccname), &cb);
+        cb += sizeof(wchar_t);
+
         if (buf && *pcb >= cb) {
             StringCbCopy(buf, *pcb, ccname);
             *pcb = cb;
@@ -3094,6 +3193,8 @@ khm_krb5_get_identity_default_ccache(khm_handle ident, wchar_t * buf, khm_size *
         wchar_t idname[KCDB_IDENT_MAXCCH_NAME];
         khm_size cb;
 
+        *pcb = cbt;
+
         cb = sizeof(idname);
         kcdb_identity_get_name(ident, idname, &cb);
 
index 67fb8dac01233e004eb63d6d761b1eb9857b39b1..410393cafa2fa7991d3052cb073d06ada27f69a9 100644 (file)
@@ -181,7 +181,7 @@ k5_handle_wmnc_notify(HWND hwnd,
     case WMNC_DIALOG_MOVE:
         {
             k5_dlg_data * d;
-                
+
             d = (k5_dlg_data *)(LONG_PTR) 
                 GetWindowLongPtr(hwnd, DWLP_USER);
 
@@ -1355,34 +1355,9 @@ k5_read_dlg_params(k5_dlg_data * d, khm_handle identity)
     d->sync = FALSE;
 }
 
-void 
-k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache)
+void
+k5_ensure_identity_ccache_is_watched(khm_handle identity, char * ccache)
 {
-
-    k5_params p;
-
-    ZeroMemory(&p, sizeof(p));
-
-    p.source_reg = K5PARAM_FM_ALL; /* we want to write all the
-                                      settings to the registry, if
-                                      necessary. */
-
-    p.renewable = d->renewable;
-    p.forwardable = d->forwardable;
-    p.proxiable = d->proxiable;
-    p.addressless = d->addressless;
-    p.publicIP = d->publicIP;
-
-    p.lifetime = (krb5_deltat) d->tc_lifetime.current;
-    p.lifetime_max = (krb5_deltat) d->tc_lifetime.max;
-    p.lifetime_min = (krb5_deltat) d->tc_lifetime.min;
-
-    p.renew_life = (krb5_deltat) d->tc_renew.current;
-    p.renew_life_max = (krb5_deltat) d->tc_renew.max;
-    p.renew_life_min = (krb5_deltat) d->tc_renew.min;
-
-    khm_krb5_set_identity_params(identity, &p);
-
     /* if we used a FILE: ccache, we should add it to FileCCList.
        Otherwise the tickets are not going to get listed. */
     do {
@@ -1462,6 +1437,37 @@ k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache)
             PFREE(mlist);
 
     } while(FALSE);
+}
+
+void 
+k5_write_dlg_params(k5_dlg_data * d, khm_handle identity, char * ccache)
+{
+
+    k5_params p;
+
+    ZeroMemory(&p, sizeof(p));
+
+    p.source_reg = K5PARAM_FM_ALL; /* we want to write all the
+                                      settings to the registry, if
+                                      necessary. */
+
+    p.renewable = d->renewable;
+    p.forwardable = d->forwardable;
+    p.proxiable = d->proxiable;
+    p.addressless = d->addressless;
+    p.publicIP = d->publicIP;
+
+    p.lifetime = (krb5_deltat) d->tc_lifetime.current;
+    p.lifetime_max = (krb5_deltat) d->tc_lifetime.max;
+    p.lifetime_min = (krb5_deltat) d->tc_lifetime.min;
+
+    p.renew_life = (krb5_deltat) d->tc_renew.current;
+    p.renew_life_max = (krb5_deltat) d->tc_renew.max;
+    p.renew_life_min = (krb5_deltat) d->tc_renew.min;
+
+    khm_krb5_set_identity_params(identity, &p);
+
+    k5_ensure_identity_ccache_is_watched(identity, ccache);
 
     /* as in k5_read_dlg_params, once we write the data in, the local
        data is no longer dirty */
@@ -2059,6 +2065,9 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                 if(g_fjob.state == FIBER_STATE_NONE) {
                     wchar_t msg[KHUI_MAXCCH_BANNER];
                     khm_size cb;
+                    int code;
+
+                    code = g_fjob.code;
 
                     /* Special case.  If the users' password has
                        expired, we force a password change dialog on
@@ -2143,7 +2152,7 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                     k5_free_kinit_job();
 
                     if (is_k5_identpro) {
-                        if (g_fjob.code == 0)
+                        if (code == 0)
                             kcdb_identity_set_flags(ident,
                                                     KCDB_IDENT_FLAG_VALID,
                                                     KCDB_IDENT_FLAG_VALID);
@@ -2832,6 +2841,9 @@ k5_msg_cred_dialog(khm_int32 msg_type,
                 imported = khm_krb5_ms2mit(NULL, (t == K5_LSAIMPORT_MATCH), TRUE,
                                            &id_imported);
                 if (imported) {
+                    if (id_imported)
+                        k5_ensure_identity_ccache_is_watched(id_imported, NULL);
+
                     khm_krb5_list_tickets(&ctx);
 
                     if (ctx)
index 4dc5b7d720a760daa562aaacc6944cde8b36f602..cb4a860d914c37c8387eafae282fe48d84f5f750 100644 (file)
@@ -27,6 +27,7 @@ Krb5Cred,KC_SPACE,0,Kerberos V Credentials Provider
     LastDefaultIdent,KC_STRING,,Last known default identity\r
     PromptCacheLifetime,KC_INT32,172800,Lifetime of the prompt cache in seconds\r
     DefaultCCName,KC_STRING,,Default CC name (only per identity)\r
+    DefaultToFileCache,KC_INT32,0,"If no DefaultCCName is specified for an identity, use a generated FILE: cache instead of an API: cache"\r
     PromptCache,KC_SPACE,0,Cache of prompts (only per identity)\r
       Name,KC_STRING,,\r
       Banner,KC_STRING,,\r