pull up r19897 from trunk
authorTom Yu <tlyu@mit.edu>
Sat, 29 Sep 2007 00:02:43 +0000 (00:02 +0000)
committerTom Yu <tlyu@mit.edu>
Sat, 29 Sep 2007 00:02:43 +0000 (00:02 +0000)
 r19897@cathode-dark-space:  jaltman | 2007-08-29 18:38:26 -0400
 ticket: new
 subject: NIM file ccache support improvements
 component: windows

 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: 5703
version_fixed: 1.6.3

git-svn-id: svn://anonsvn.mit.edu/krb5/branches/krb5-1-6@20009 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