/* * CCache-glue.c * * This file contains implementations of krb4 credentials cache operations in terms * of the CCache API (). * * $Header$ */ #include "krb.h" #include "krb4int.h" #if !defined (USE_CCAPI) || !USE_CCAPI #error "Cannot use CCache glue without the CCAPI!" #endif #ifdef USE_LOGIN_LIBRARY #include #endif /* USE_LOGIN_LIBRARY */ #include #include #include /* * The following functions are part of the KfM ABI. * They are deprecated, so they only appear here, not in krb.h. * * Do not change the ABI of these functions! */ int KRB5_CALLCONV krb_get_num_cred(void); int KRB5_CALLCONV krb_get_nth_cred(char *, char *, char *, int); int KRB5_CALLCONV krb_delete_cred(char *, char *,char *); int KRB5_CALLCONV dest_all_tkts(void); /* Internal functions */ static void UpdateDefaultCache (void); /* * The way Kerberos v4 normally works is that at any given point in time there is a * file where all the tickets go, determined by an environment variable. If a user kinits * to a new principal, the existing tickets are replaced with new ones. At any point in time, there is a * "current" or "default" principal, which is determined by the principal associated with * the current ticket file. * * In the CCache API implementation, this corresponds to always having a "default" * or "current" named cache. The default principal then corresponds to that cache. * * Unfortunately, Kerberos v4 also has this notion that the default cache exists (in the sense * that its name is known) even before the actual file has been created. * * In addition to this, we cannot make the default cache system-wide global, because then * we get all sorts of interesting scenarios in which context switches between processes * can cause credentials to be stored in wrong caches. * * To solve all the problems, we have to emulate the concept of an environment variable, * by having a system-wide concept of what a default credentials cache is; then, we copy * the system-wide value into the per-process value when the application starts up. * * However, in order to allow applications to be able to sanely handle the user model we * want to support, in which the user has some way of selecting the system-wide default * user _without_ quitting and relaunching all applications (this is also necessary for * KClient support), calls had to be added to the Kerberos v4 library to reset the * per-process cached value of default cache. */ /* * Name of the default cache */ char* gDefaultCacheName = NULL; /* * Initialize credentials cache * * Creating the cache will blow away an existing one. The assumption is that * whoever called us made sure that the one that we blow away if it exists * is the right one to blow away. */ int KRB5_CALLCONV krb_in_tkt ( char* pname, char* pinst, char* realm) { char principal [MAX_K_NAME_SZ + 1]; cc_int32 err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (err == ccNoError) { snprintf (principal, sizeof(principal), "%s%s%s@%s", pname, (pinst [0] == '\0') ? "" : ".", pinst, realm); } if (err == ccNoError) { err = cc_context_create_ccache (cc_context, TKT_FILE, cc_credentials_v4, principal, &ccache); } if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (err != ccNoError) return KFAILURE; else return KSUCCESS; } int KRB5_CALLCONV krb_save_credentials( char *service, char *instance, char *realm, C_Block session, int lifetime, int kvno, KTEXT ticket, long issue_date) { return krb4int_save_credentials_addr(service, instance, realm, session, lifetime, kvno, ticket, issue_date, 0); } /* * Store a ticket into the default credentials cache * cache must exist (if it didn't exist, it would have been created by in_tkt) */ int krb4int_save_credentials_addr( char* service, char* instance, char* realm, C_Block session, int lifetime, int kvno, KTEXT ticket, KRB4_32 issue_date, KRB_UINT32 local_address) { cc_int32 cc_err = ccNoError; int kerr = KSUCCESS; cc_credentials_v4_t v4creds; cc_credentials_union creds; cc_ccache_t ccache = NULL; cc_string_t principal; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { /* First try existing cache */ cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { /* Now we have a cache. Fill out the credentials and put them in the cache. */ /* To fill out the credentials, we need the principal */ cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal); } if (cc_err == ccNoError) { kerr = kname_parse (v4creds.principal, v4creds.principal_instance, v4creds.realm, (char*) principal -> data); cc_string_release (principal); } if ((cc_err == ccNoError) && (kerr == KSUCCESS)) { strncpy (v4creds.service, service, SNAME_SZ); strncpy (v4creds.service_instance, instance, INST_SZ); strncpy (v4creds.realm, realm, REALM_SZ); memmove (v4creds.session_key, session, sizeof (C_Block)); v4creds.kvno = kvno; v4creds.string_to_key_type = cc_v4_stk_unknown; v4creds.issue_date = issue_date; v4creds.address = local_address; v4creds.lifetime = lifetime; v4creds.ticket_size = ticket -> length; memmove (v4creds.ticket, ticket -> dat, ticket -> length); creds.version = cc_credentials_v4; creds.credentials.credentials_v4 = &v4creds; cc_err = cc_ccache_store_credentials (ccache, &creds); } if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (kerr != KSUCCESS) return kerr; if (cc_err != ccNoError) return KFAILURE; else return KSUCCESS; } /* * Credentials file -> realm mapping * * Determine the realm by opening the named cache and parsing realm from the principal */ int KRB5_CALLCONV krb_get_tf_realm ( const char* ticket_file, char* realm) { cc_string_t principal; char pname [ANAME_SZ]; char pinst [INST_SZ]; char prealm [REALM_SZ]; int kerr = KSUCCESS; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version = 0; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, ticket_file, &ccache); } if (cc_err == ccNoError) { cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal); } if (cc_err == ccNoError) { /* found cache. get princiapl and parse it */ kerr = kname_parse (pname, pinst, prealm, (char*) principal -> data); cc_string_release (principal); } if ((cc_err == ccNoError) && (kerr == KSUCCESS)) { strcpy (realm, prealm); } if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (kerr != KSUCCESS) return kerr; if (cc_err != ccNoError) return GC_NOTKT; else return KSUCCESS; } /* * Credentials file -> name, instance, realm mapping */ int KRB5_CALLCONV krb_get_tf_fullname ( const char* ticket_file, char* name, char* instance, char* realm) { cc_string_t principal; int kerr = KSUCCESS; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, ticket_file, &ccache); } if (cc_err == ccNoError) { /* found cache. get principal and parse it */ cc_err = cc_ccache_get_principal (ccache, cc_credentials_v4, &principal); } if (cc_err == ccNoError) { kerr = kname_parse (name, instance, realm, (char*) principal -> data); cc_string_release (principal); } if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (kerr != KSUCCESS) return kerr; if (cc_err != ccNoError) return GC_NOTKT; else return KSUCCESS; } /* * Retrieval from credentials cache */ int KRB5_CALLCONV krb_get_cred ( char* service, char* instance, char* realm, CREDENTIALS* creds) { int kerr = KSUCCESS; cc_int32 cc_err = ccNoError; cc_credentials_t theCreds = NULL; cc_credentials_iterator_t iterator = NULL; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; #ifdef USE_LOGIN_LIBRARY // If we are requesting a tgt, prompt for it if (strncmp (service, KRB_TICKET_GRANTING_TICKET, ANAME_SZ) == 0) { OSStatus err; char *cacheName; KLPrincipal outPrincipal; err = __KLInternalAcquireInitialTicketsForCache (TKT_FILE, kerberosVersion_V4, NULL, &outPrincipal, &cacheName); if (err == klNoErr) { krb_set_tkt_string (cacheName); // Tickets for the krb4 principal went here KLDisposeString (cacheName); KLDisposePrincipal (outPrincipal); } else { return GC_NOTKT; } } #endif /* USE_LOGIN_LIBRARY */ cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator); } if (cc_err == ccNoError) { for (;;) { /* get next creds */ cc_err = cc_credentials_iterator_next (iterator, &theCreds); if (cc_err == ccIteratorEnd) { kerr = GC_NOTKT; break; } if (cc_err != ccNoError) { kerr = KFAILURE; break; } /* version, service, instance, realm check */ if ((theCreds -> data -> version == cc_credentials_v4) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> service, service) == 0) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> service_instance, instance) == 0) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> realm, realm) == 0)) { /* Match! */ strcpy (creds -> service, service); strcpy (creds -> instance, instance); strcpy (creds -> realm, realm); memmove (creds -> session, theCreds -> data -> credentials.credentials_v4 -> session_key, sizeof (C_Block)); creds -> lifetime = theCreds -> data -> credentials.credentials_v4 -> lifetime; creds -> kvno = theCreds -> data -> credentials.credentials_v4 -> kvno; creds -> ticket_st.length = theCreds -> data -> credentials.credentials_v4 -> ticket_size; memmove (creds -> ticket_st.dat, theCreds -> data -> credentials.credentials_v4 -> ticket, creds -> ticket_st.length); creds -> issue_date = theCreds -> data -> credentials.credentials_v4 -> issue_date; strcpy (creds -> pname, theCreds -> data -> credentials.credentials_v4 -> principal); strcpy (creds -> pinst, theCreds -> data -> credentials.credentials_v4 -> principal_instance); creds -> stk_type = theCreds -> data -> credentials.credentials_v4 -> string_to_key_type; cc_credentials_release (theCreds); kerr = KSUCCESS; break; } else { cc_credentials_release (theCreds); } } } if (iterator != NULL) cc_credentials_iterator_release (iterator); if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (kerr != KSUCCESS) return kerr; if (cc_err != ccNoError) return GC_NOTKT; else return KSUCCESS; } /* * Getting name of default credentials cache */ const char* KRB5_CALLCONV tkt_string (void) { if (gDefaultCacheName == NULL) { UpdateDefaultCache (); } return gDefaultCacheName; } /* * Synchronize default cache for this process with system default cache */ static void UpdateDefaultCache (void) { cc_string_t name; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_get_default_ccache_name (cc_context, &name); } if (cc_err == ccNoError) { krb_set_tkt_string ((char*) name -> data); cc_string_release (name); } if (cc_context != NULL) cc_context_release (cc_context); } /* * Setting name of default credentials cache */ void krb_set_tkt_string ( const char* val) { /* If we get called with the return value of tkt_string, we shouldn't dispose of the input string */ if (val != gDefaultCacheName) { if (gDefaultCacheName != NULL) free (gDefaultCacheName); gDefaultCacheName = malloc (strlen (val) + 1); if (gDefaultCacheName != NULL) strcpy (gDefaultCacheName, val); } } /* * Destroy credentials file * * Implementation in dest_tkt.c */ int KRB5_CALLCONV dest_tkt (void) { cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { cc_ccache_destroy (ccache); } if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (cc_err != ccNoError) return RET_TKFIL; else return KSUCCESS; } /* * The following functions are not part of the standard Kerberos v4 API. * They were created for Mac implementation, and used by admin tools * such as CNS-Config. */ /* * Number of credentials in credentials cache */ int KRB5_CALLCONV krb_get_num_cred (void) { cc_credentials_t theCreds = NULL; int count = 0; cc_credentials_iterator_t iterator = NULL; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator); } if (cc_err == ccNoError) { for (;;) { /* get next creds */ cc_err = cc_credentials_iterator_next (iterator, &theCreds); if (cc_err != ccNoError) break; if (theCreds -> data -> version == cc_credentials_v4) count++; cc_credentials_release (theCreds); } } if (iterator != NULL) cc_credentials_iterator_release (iterator); if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (cc_err != ccNoError) return 0; else return count; } /* * Retrieval from credentials file * This function is _not_!! well-defined under CCache API, because * there is no guarantee about order of credentials remaining the same. */ int KRB5_CALLCONV krb_get_nth_cred ( char* sname, char* sinstance, char* srealm, int n) { cc_credentials_t theCreds = NULL; int count = 0; cc_credentials_iterator_t iterator = NULL; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; if (n < 1) return KFAILURE; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator); } if (cc_err == ccNoError) { for (count = 0; count < n;) { /* get next creds */ cc_err = cc_credentials_iterator_next (iterator, &theCreds); if (cc_err != ccNoError) break; if (theCreds -> data -> version == cc_credentials_v4) count++; if (count < n - 1) cc_credentials_release (theCreds); } } if (cc_err == ccNoError) { strcpy (sname, theCreds -> data -> credentials.credentials_v4 -> service); strcpy (sinstance, theCreds -> data -> credentials.credentials_v4 -> service_instance); strcpy (srealm, theCreds -> data -> credentials.credentials_v4 -> realm); } if (theCreds != NULL) cc_credentials_release (theCreds); if (iterator != NULL) cc_credentials_iterator_release (iterator); if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (cc_err != ccNoError) return KFAILURE; else return KSUCCESS; } /* * Deletion from credentials file */ int KRB5_CALLCONV krb_delete_cred ( char* sname, char* sinstance, char* srealm) { cc_credentials_t theCreds = NULL; cc_credentials_iterator_t iterator = NULL; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_open_ccache (cc_context, TKT_FILE, &ccache); } if (cc_err == ccNoError) { cc_err = cc_ccache_new_credentials_iterator (ccache, &iterator); } if (cc_err == ccNoError) { for (;;) { /* get next creds */ cc_err = cc_credentials_iterator_next (iterator, &theCreds); if (cc_err != ccNoError) { break; } if ((theCreds -> data -> version == cc_credentials_v4) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> service, sname) == 0) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> service_instance, sinstance) == 0) && (strcmp (theCreds -> data -> credentials.credentials_v4 -> realm, srealm) == 0)) { cc_ccache_remove_credentials (ccache, theCreds); cc_credentials_release (theCreds); break; } cc_credentials_release (theCreds); } } if (iterator != NULL) cc_credentials_iterator_release (iterator); if (ccache != NULL) cc_ccache_release (ccache); if (cc_context != NULL) cc_context_release (cc_context); if (cc_err != ccNoError) return KFAILURE; else return KSUCCESS; } /* * Destroy all credential caches * * Implementation in memcache.c */ int KRB5_CALLCONV dest_all_tkts (void) { int count = 0; cc_ccache_iterator_t iterator = NULL; cc_int32 cc_err = ccNoError; cc_context_t cc_context = NULL; cc_int32 cc_version; cc_ccache_t ccache = NULL; cc_err = cc_initialize (&cc_context, ccapi_version_3, &cc_version, NULL); if (cc_err == ccNoError) { cc_err = cc_context_new_ccache_iterator (cc_context, &iterator); } if (cc_err == ccNoError) { for (;;) { /* get next ccache */ cc_err = cc_ccache_iterator_next (iterator, &ccache); if (cc_err != ccNoError) break; cc_ccache_destroy (ccache); count++; } } if (iterator != NULL) cc_credentials_iterator_release (iterator); if (cc_context != NULL) cc_context_release (cc_context); if ((cc_err == ccIteratorEnd) && (count == 0)) { /* first time, nothing to destroy */ return KFAILURE; } else { if (cc_err == ccIteratorEnd) { /* done */ return KSUCCESS; } else { /* error */ return KFAILURE; } } }