From fe2dfe83a88fbc2ad321e0d042ded79a75b448c5 Mon Sep 17 00:00:00 2001 From: Love Hornquist Astrand Date: Sat, 21 Nov 2009 23:54:00 -0800 Subject: [PATCH] clean up and make work, require libheim-ipcs --- kcm/acl.c | 57 ++-- kcm/acquire.c | 2 - kcm/cache.c | 225 ++++++++----- kcm/client.c | 26 +- kcm/config.c | 18 +- kcm/connect.c | 667 +++------------------------------------ kcm/events.c | 2 +- kcm/headers.h | 8 +- kcm/kcm_locl.h | 15 + kcm/protocol.c | 831 +++++++++++++++++++++++++++++++++++++++++++++---- 10 files changed, 1022 insertions(+), 829 deletions(-) diff --git a/kcm/acl.c b/kcm/acl.c index 2823260ca..5102c1335 100644 --- a/kcm/acl.c +++ b/kcm/acl.c @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,8 +34,6 @@ #include "kcm_locl.h" -RCSID("$Id$"); - krb5_error_code kcm_access(krb5_context context, kcm_client *client, @@ -58,6 +58,8 @@ kcm_access(krb5_context context, case KCM_OP_GET_INITIAL_TICKET: case KCM_OP_GET_TICKET: case KCM_OP_MOVE_CACHE: + case KCM_OP_SET_DEFAULT_CACHE: + case KCM_OP_SET_KDC_OFFSET: write_p = 1; read_p = 0; break; @@ -67,13 +69,18 @@ kcm_access(krb5_context context, case KCM_OP_GEN_NEW: case KCM_OP_RETRIEVE: case KCM_OP_GET_PRINCIPAL: - case KCM_OP_GET_FIRST: - case KCM_OP_GET_NEXT: - case KCM_OP_END_GET: - case KCM_OP_MAX: + case KCM_OP_GET_CRED_UUID_LIST: + case KCM_OP_GET_CRED_BY_UUID: + case KCM_OP_GET_CACHE_UUID_LIST: + case KCM_OP_GET_CACHE_BY_UUID: + case KCM_OP_GET_DEFAULT_CACHE: + case KCM_OP_GET_KDC_OFFSET: write_p = 0; read_p = 1; break; + default: + ret = KRB5_FCC_PERM; + goto out; } if (ccache->flags & KCM_FLAGS_OWNER_IS_SYSTEM) { @@ -87,33 +94,45 @@ kcm_access(krb5_context context, } /* Let root always read system caches */ - if (client->uid == 0) { + if (CLIENT_IS_ROOT(client)) { ret = 0; goto out; } } - mask = 0; + /* start out with "other" mask */ + mask = S_IROTH|S_IWOTH; - /* Root may do whatever they like */ - if (client->uid == ccache->uid || CLIENT_IS_ROOT(client)) { + /* root can do anything */ + if (CLIENT_IS_ROOT(client)) { if (read_p) - mask |= S_IRUSR; + mask |= S_IRUSR|S_IRGRP|S_IROTH; if (write_p) - mask |= S_IWUSR; - } else if (client->gid == ccache->gid || CLIENT_IS_ROOT(client)) { - if (read_p) - mask |= S_IRGRP; - if (write_p) - mask |= S_IWGRP; - } else { + mask |= S_IWUSR|S_IWGRP|S_IWOTH; + } + /* same session same as owner */ + if (kcm_is_same_session(client, ccache->uid, ccache->session)) { if (read_p) mask |= S_IROTH; if (write_p) mask |= S_IWOTH; } + /* owner */ + if (client->uid == ccache->uid) { + if (read_p) + mask |= S_IRUSR; + if (write_p) + mask |= S_IWUSR; + } + /* group */ + if (client->gid == ccache->gid) { + if (read_p) + mask |= S_IRGRP; + if (write_p) + mask |= S_IWGRP; + } - ret = ((ccache->mode & mask) == mask) ? 0 : KRB5_FCC_PERM; + ret = (ccache->mode & mask) ? 0 : KRB5_FCC_PERM; out: if (ret) { diff --git a/kcm/acquire.c b/kcm/acquire.c index 6d211c467..e5423cd69 100644 --- a/kcm/acquire.c +++ b/kcm/acquire.c @@ -32,8 +32,6 @@ #include "kcm_locl.h" -RCSID("$Id$"); - /* * Get a new ticket using a keytab/cached key and swap it into * an existing redentials cache diff --git a/kcm/cache.c b/kcm/cache.c index 74e65be06..8a27ba0c6 100644 --- a/kcm/cache.c +++ b/kcm/cache.c @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,10 +34,8 @@ #include "kcm_locl.h" -RCSID("$Id$"); - -static HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; -static kcm_ccache_data *ccache_head = NULL; +HEIMDAL_MUTEX ccache_mutex = HEIMDAL_MUTEX_INITIALIZER; +kcm_ccache_data *ccache_head = NULL; static unsigned int ccache_nextid = 0; char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid) @@ -52,10 +52,10 @@ char *kcm_ccache_nextid(pid_t pid, uid_t uid, gid_t gid) return name; } -static krb5_error_code -kcm_ccache_resolve_internal(krb5_context context, - const char *name, - kcm_ccache *ccache) +krb5_error_code +kcm_ccache_resolve(krb5_context context, + const char *name, + kcm_ccache *ccache) { kcm_ccache p; krb5_error_code ret; @@ -85,6 +85,66 @@ kcm_ccache_resolve_internal(krb5_context context, return ret; } +krb5_error_code +kcm_ccache_resolve_by_uuid(krb5_context context, + kcmuuid_t uuid, + kcm_ccache *ccache) +{ + kcm_ccache p; + krb5_error_code ret; + + *ccache = NULL; + + ret = KRB5_FCC_NOFILE; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + + for (p = ccache_head; p != NULL; p = p->next) { + if ((p->flags & KCM_FLAGS_VALID) == 0) + continue; + if (memcmp(p->uuid, uuid, sizeof(uuid)) == 0) { + ret = 0; + break; + } + } + + if (ret == 0) { + kcm_retain_ccache(context, p); + *ccache = p; + } + + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + return ret; +} + +krb5_error_code +kcm_ccache_get_uuids(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *sp) +{ + krb5_error_code ret; + kcm_ccache p; + + ret = KRB5_FCC_NOFILE; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + + for (p = ccache_head; p != NULL; p = p->next) { + if ((p->flags & KCM_FLAGS_VALID) == 0) + continue; + ret = kcm_access(context, client, opcode, p); + if (ret) { + ret = 0; + continue; + } + krb5_storage_write(sp, p->uuid, sizeof(p->uuid)); + } + + HEIMDAL_MUTEX_unlock(&ccache_mutex); + + return ret; +} + + krb5_error_code kcm_debug_ccache(krb5_context context) { kcm_ccache p; @@ -125,10 +185,48 @@ krb5_error_code kcm_debug_ccache(krb5_context context) return 0; } -static krb5_error_code -kcm_ccache_destroy_internal(krb5_context context, const char *name) +static void +kcm_free_ccache_data_internal(krb5_context context, + kcm_ccache_data *cache) { - kcm_ccache *p; + KCM_ASSERT_VALID(cache); + + if (cache->name != NULL) { + free(cache->name); + cache->name = NULL; + } + + if (cache->flags & KCM_FLAGS_USE_KEYTAB) { + krb5_kt_close(context, cache->key.keytab); + cache->key.keytab = NULL; + } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) { + krb5_free_keyblock_contents(context, &cache->key.keyblock); + krb5_keyblock_zero(&cache->key.keyblock); + } + + cache->flags = 0; + cache->mode = 0; + cache->uid = -1; + cache->gid = -1; + cache->session = -1; + + kcm_zero_ccache_data_internal(context, cache); + + cache->tkt_life = 0; + cache->renew_life = 0; + + cache->next = NULL; + cache->refcnt = 0; + + HEIMDAL_MUTEX_unlock(&cache->mutex); + HEIMDAL_MUTEX_destroy(&cache->mutex); +} + + +krb5_error_code +kcm_ccache_destroy(krb5_context context, const char *name) +{ + kcm_ccache *p, ccache; krb5_error_code ret; ret = KRB5_FCC_NOFILE; @@ -142,11 +240,18 @@ kcm_ccache_destroy_internal(krb5_context context, const char *name) break; } } - if (ret) goto out; - kcm_release_ccache(context, p); + if ((*p)->refcnt != 1) { + ret = EAGAIN; + goto out; + } + + ccache = *p; + *p = (*p)->next; + kcm_free_ccache_data_internal(context, ccache); + free(ccache); out: HEIMDAL_MUTEX_unlock(&ccache_mutex); @@ -195,6 +300,8 @@ kcm_ccache_alloc(krb5_context context, new_slot = 1; } + RAND_bytes(slot->uuid, sizeof(slot->uuid)); + slot->name = strdup(name); if (slot->name == NULL) { ret = KRB5_CC_NOMEM; @@ -299,44 +406,6 @@ kcm_zero_ccache_data(krb5_context context, return ret; } -static krb5_error_code -kcm_free_ccache_data_internal(krb5_context context, - kcm_ccache_data *cache) -{ - KCM_ASSERT_VALID(cache); - - if (cache->name != NULL) { - free(cache->name); - cache->name = NULL; - } - - if (cache->flags & KCM_FLAGS_USE_KEYTAB) { - krb5_kt_close(context, cache->key.keytab); - cache->key.keytab = NULL; - } else if (cache->flags & KCM_FLAGS_USE_CACHED_KEY) { - krb5_free_keyblock_contents(context, &cache->key.keyblock); - krb5_keyblock_zero(&cache->key.keyblock); - } - - cache->flags = 0; - cache->mode = 0; - cache->uid = -1; - cache->gid = -1; - - kcm_zero_ccache_data_internal(context, cache); - - cache->tkt_life = 0; - cache->renew_life = 0; - - cache->next = NULL; - cache->refcnt = 0; - - HEIMDAL_MUTEX_unlock(&cache->mutex); - HEIMDAL_MUTEX_destroy(&cache->mutex); - - return 0; -} - krb5_error_code kcm_retain_ccache(krb5_context context, kcm_ccache ccache) @@ -351,26 +420,19 @@ kcm_retain_ccache(krb5_context context, } krb5_error_code -kcm_release_ccache(krb5_context context, - kcm_ccache *ccache) +kcm_release_ccache(krb5_context context, kcm_ccache c) { - kcm_ccache c = *ccache; krb5_error_code ret = 0; KCM_ASSERT_VALID(c); HEIMDAL_MUTEX_lock(&c->mutex); if (c->refcnt == 1) { - ret = kcm_free_ccache_data_internal(context, c); - if (ret == 0) - free(c); } else { c->refcnt--; HEIMDAL_MUTEX_unlock(&c->mutex); } - *ccache = NULL; - return ret; } @@ -414,29 +476,6 @@ kcm_ccache_new(krb5_context context, return ret; } -krb5_error_code -kcm_ccache_resolve(krb5_context context, - const char *name, - kcm_ccache *ccache) -{ - krb5_error_code ret; - - ret = kcm_ccache_resolve_internal(context, name, ccache); - - return ret; -} - -krb5_error_code -kcm_ccache_destroy(krb5_context context, - const char *name) -{ - krb5_error_code ret; - - ret = kcm_ccache_destroy_internal(context, name); - - return ret; -} - krb5_error_code kcm_ccache_destroy_if_empty(krb5_context context, kcm_ccache ccache) @@ -446,7 +485,7 @@ kcm_ccache_destroy_if_empty(krb5_context context, KCM_ASSERT_VALID(ccache); if (ccache->creds == NULL) { - ret = kcm_ccache_destroy_internal(context, ccache->name); + ret = kcm_ccache_destroy(context, ccache->name); } else ret = 0; @@ -541,6 +580,8 @@ kcm_ccache_remove_cred_internal(krb5_context context, krb5_free_cred_contents(context, &cred->cred); free(cred); ret = 0; + if (*c == NULL) + break; } } @@ -612,3 +653,21 @@ kcm_ccache_retrieve_cred(krb5_context context, return ret; } + +char * +kcm_ccache_first_name(kcm_client *client) +{ + kcm_ccache p; + char *name = NULL; + + HEIMDAL_MUTEX_lock(&ccache_mutex); + + for (p = ccache_head; p != NULL; p = p->next) { + if (kcm_is_same_session(client, p->uid, p->session)) + break; + } + if (p) + name = strdup(p->name); + HEIMDAL_MUTEX_unlock(&ccache_mutex); + return name; +} diff --git a/kcm/client.c b/kcm/client.c index a6c83b45f..9d10ad65e 100644 --- a/kcm/client.c +++ b/kcm/client.c @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -33,8 +35,6 @@ #include "kcm_locl.h" #include -RCSID("$Id$"); - krb5_error_code kcm_ccache_resolve_client(krb5_context context, kcm_client *client, @@ -54,7 +54,7 @@ kcm_ccache_resolve_client(krb5_context context, ret = kcm_access(context, client, opcode, *ccache); if (ret) { ret = KRB5_FCC_NOFILE; /* don't disclose */ - kcm_release_ccache(context, ccache); + kcm_release_ccache(context, *ccache); } return ret; @@ -76,19 +76,12 @@ kcm_ccache_destroy_client(krb5_context context, } ret = kcm_access(context, client, KCM_OP_DESTROY, ccache); - if (ret) { - kcm_release_ccache(context, &ccache); + kcm_cleanup_events(context, ccache); + kcm_release_ccache(context, ccache); + if (ret) return ret; - } - ret = kcm_ccache_destroy(context, ccache->name); - if (ret == 0) { - /* don't leave any events dangling */ - kcm_cleanup_events(context, ccache); - } - - kcm_release_ccache(context, &ccache); - return ret; + return kcm_ccache_destroy(context, name); } krb5_error_code @@ -142,12 +135,13 @@ kcm_ccache_new_client(krb5_context context, /* bind to current client */ ccache->uid = client->uid; ccache->gid = client->gid; + ccache->session = client->session; } else { ret = kcm_zero_ccache_data(context, ccache); if (ret) { kcm_log(1, "Failed to empty cache %s: %s", name, krb5_get_err_text(context, ret)); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } kcm_cleanup_events(context, ccache); @@ -155,7 +149,7 @@ kcm_ccache_new_client(krb5_context context, ret = kcm_access(context, client, KCM_OP_INITIALIZE, ccache); if (ret) { - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); kcm_ccache_destroy(context, name); return ret; } diff --git a/kcm/config.c b/kcm/config.c index d7c5e1688..c4b732fc3 100644 --- a/kcm/config.c +++ b/kcm/config.c @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -34,8 +36,6 @@ #include #include -RCSID("$Id$"); - static const char *config_file; /* location of kcm config file */ size_t max_request = 0; /* maximal size of a request */ @@ -60,7 +60,7 @@ static const char *system_group = NULL; static const char *renew_life = NULL; static const char *ticket_life = NULL; -int disallow_getting_krbtgt = -1; +int disallow_getting_krbtgt = 0; int name_constraints = -1; static int help_flag; @@ -240,7 +240,7 @@ ccache_init_system(void) ret = krb5_parse_name(kcm_context, system_principal, &ccache->client); if (ret) { - kcm_release_ccache(kcm_context, &ccache); + kcm_release_ccache(kcm_context, ccache); return ret; } @@ -250,7 +250,7 @@ ccache_init_system(void) if (system_server != NULL) { ret = krb5_parse_name(kcm_context, system_server, &ccache->server); if (ret) { - kcm_release_ccache(kcm_context, &ccache); + kcm_release_ccache(kcm_context, ccache); return ret; } } @@ -264,7 +264,7 @@ ccache_init_system(void) ret = krb5_kt_default(kcm_context, &ccache->key.keytab); } if (ret) { - kcm_release_ccache(kcm_context, &ccache); + kcm_release_ccache(kcm_context, ccache); return ret; } @@ -277,7 +277,7 @@ ccache_init_system(void) if (renew_life != NULL) { ccache->renew_life = parse_time(renew_life, "s"); if (ccache->renew_life < 0) { - kcm_release_ccache(kcm_context, &ccache); + kcm_release_ccache(kcm_context, ccache); return EINVAL; } } @@ -288,7 +288,7 @@ ccache_init_system(void) if (ticket_life != NULL) { ccache->tkt_life = parse_time(ticket_life, "s"); if (ccache->tkt_life < 0) { - kcm_release_ccache(kcm_context, &ccache); + kcm_release_ccache(kcm_context, ccache); return EINVAL; } } @@ -314,7 +314,7 @@ ccache_init_system(void) /* enqueue default actions for credentials cache */ ret = kcm_ccache_enqueue_default(kcm_context, ccache, NULL); - kcm_release_ccache(kcm_context, &ccache); /* retained by event queue */ + kcm_release_ccache(kcm_context, ccache); /* retained by event queue */ return ret; } diff --git a/kcm/connect.c b/kcm/connect.c index 9c85d8fce..8147f265f 100644 --- a/kcm/connect.c +++ b/kcm/connect.c @@ -3,6 +3,8 @@ * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -32,657 +34,62 @@ */ #include "kcm_locl.h" +#include -RCSID("$Id$"); - -struct descr { - int s; - int type; - char *path; - unsigned char *buf; - size_t size; - size_t len; - time_t timeout; - struct sockaddr_storage __ss; - struct sockaddr *sa; - socklen_t sock_len; +static void +kcm_service(void *ctx, const heim_idata *req, + const heim_icred cred, + heim_ipc_complete complete, + heim_sipc_call cctx) +{ kcm_client peercred; -}; + krb5_error_code ret; + krb5_data request, rep; + unsigned char *buf; + size_t len; -static void -init_descr(struct descr *d) -{ - memset(d, 0, sizeof(*d)); - d->sa = (struct sockaddr *)&d->__ss; - d->s = -1; -} + krb5_data_zero(&rep); -/* - * re-initialize all `n' ->sa in `d'. - */ + peercred.uid = heim_ipc_cred_get_uid(cred); + peercred.gid = heim_ipc_cred_get_gid(cred); + peercred.pid = heim_ipc_cred_get_pid(cred); + peercred.session = heim_ipc_cred_get_session(cred); -static void -reinit_descrs (struct descr *d, int n) -{ - int i; - - for (i = 0; i < n; ++i) - d[i].sa = (struct sockaddr *)&d[i].__ss; -} - -/* - * Update peer credentials from socket. - * - * SCM_CREDS can only be updated the first time there is read data to - * read from the filedescriptor, so if we read do it before this - * point, the cred data might not be is not there yet. - */ - -static int -update_client_creds(int s, kcm_client *peer) -{ -#ifdef GETPEERUCRED - /* Solaris 10 */ - { - ucred_t *peercred; - - if (getpeerucred(s, &peercred) != 0) { - peer->uid = ucred_geteuid(peercred); - peer->gid = ucred_getegid(peercred); - peer->pid = 0; - ucred_free(peercred); - return 0; - } - } -#endif -#ifdef GETPEEREID - /* FreeBSD, OpenBSD */ - { - uid_t uid; - gid_t gid; - - if (getpeereid(s, &uid, &gid) == 0) { - peer->uid = uid; - peer->gid = gid; - peer->pid = 0; - return 0; - } - } -#endif -#ifdef SO_PEERCRED - /* Linux */ - { - struct ucred pc; - socklen_t pclen = sizeof(pc); - - if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) { - peer->uid = pc.uid; - peer->gid = pc.gid; - peer->pid = pc.pid; - return 0; - } - } -#endif -#if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION) - { - struct xucred peercred; - socklen_t peercredlen = sizeof(peercred); - - if (getsockopt(s, LOCAL_PEERCRED, 1, - (void *)&peercred, &peercredlen) == 0 - && peercred.cr_version == XUCRED_VERSION) - { - peer->uid = peercred.cr_uid; - peer->gid = peercred.cr_gid; - peer->pid = 0; - return 0; - } - } -#endif -#if defined(SOCKCREDSIZE) && defined(SCM_CREDS) - /* NetBSD */ - if (peer->uid == -1) { - struct msghdr msg; - socklen_t crmsgsize; - void *crmsg; - struct cmsghdr *cmp; - struct sockcred *sc; - - memset(&msg, 0, sizeof(msg)); - crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); - if (crmsgsize == 0) - return 1 ; - - crmsg = malloc(crmsgsize); - if (crmsg == NULL) - goto failed_scm_creds; - - memset(crmsg, 0, crmsgsize); - - msg.msg_control = crmsg; - msg.msg_controllen = crmsgsize; - - if (recvmsg(s, &msg, 0) < 0) { - free(crmsg); - goto failed_scm_creds; - } - - if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { - free(crmsg); - goto failed_scm_creds; - } - - cmp = CMSG_FIRSTHDR(&msg); - if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { - free(crmsg); - goto failed_scm_creds; - } - - sc = (struct sockcred *)(void *)CMSG_DATA(cmp); - - peer->uid = sc->sc_euid; - peer->gid = sc->sc_egid; - peer->pid = 0; - - free(crmsg); - return 0; - } else { - /* we already got the cred, just return it */ - return 0; - } - failed_scm_creds: -#endif - krb5_warn(kcm_context, errno, "failed to determine peer identity"); - return 1; -} - - -/* - * Create the socket (family, type, port) in `d' - */ - -static void -init_socket(struct descr *d) -{ - struct sockaddr_un un; - struct sockaddr *sa = (struct sockaddr *)&un; - krb5_socklen_t sa_size = sizeof(un); - - init_descr (d); - - un.sun_family = AF_UNIX; - - if (socket_path != NULL) - d->path = socket_path; - else - d->path = _PATH_KCM_SOCKET; - - strlcpy(un.sun_path, d->path, sizeof(un.sun_path)); - - d->s = socket(AF_UNIX, SOCK_STREAM, 0); - if (d->s < 0){ - krb5_warn(kcm_context, errno, "socket(%d, %d, 0)", AF_UNIX, SOCK_STREAM); - d->s = -1; - return; - } -#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR) - { - int one = 1; - setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); - } -#endif -#ifdef LOCAL_CREDS - { - int one = 1; - setsockopt(d->s, 0, LOCAL_CREDS, (void *)&one, sizeof(one)); - } -#endif - - d->type = SOCK_STREAM; - - unlink(d->path); - - if (bind(d->s, sa, sa_size) < 0) { - krb5_warn(kcm_context, errno, "bind %s", un.sun_path); - close(d->s); - d->s = -1; - return; - } - - if (listen(d->s, SOMAXCONN) < 0) { - krb5_warn(kcm_context, errno, "listen %s", un.sun_path); - close(d->s); - d->s = -1; - return; - } - - chmod(d->path, 0777); - - return; -} - -/* - * Allocate descriptors for all the sockets that we should listen on - * and return the number of them. - */ - -static int -init_sockets(struct descr **desc) -{ - struct descr *d; - size_t num = 0; - - d = (struct descr *)malloc(sizeof(*d)); - if (d == NULL) { - krb5_errx(kcm_context, 1, "malloc failed"); - } - - init_socket(d); - if (d->s != -1) { - kcm_log(5, "listening on domain socket %s", d->path); - num++; - } - - reinit_descrs (d, num); - *desc = d; - - return num; -} - -/* - * handle the request in `buf, len', from `addr' (or `from' as a string), - * sending a reply in `reply'. - */ - -static int -process_request(unsigned char *buf, - size_t len, - krb5_data *reply, - kcm_client *client) -{ - krb5_data request; - - if (len < 4) { + if (req->length < 4) { kcm_log(1, "malformed request from process %d (too short)", - client->pid); - return -1; + peercred.pid); + (*complete)(cctx, EINVAL, NULL); + return; } + buf = req->data; + len = req->length; + if (buf[0] != KCM_PROTOCOL_VERSION_MAJOR || buf[1] != KCM_PROTOCOL_VERSION_MINOR) { kcm_log(1, "incorrect protocol version %d.%d from process %d", - buf[0], buf[1], client->pid); - return -1; + buf[0], buf[1], peercred.pid); + (*complete)(cctx, EINVAL, NULL); + return; } - buf += 2; - len -= 2; - + request.data = buf + 2; + request.length = len - 2; + /* buf is now pointing at opcode */ - request.data = buf; - request.length = len; + ret = kcm_dispatch(kcm_context, &peercred, &request, &rep); - return kcm_dispatch(kcm_context, client, &request, reply); + (*complete)(cctx, ret, &rep); + krb5_data_free(&rep); } -/* - * Handle the request in `buf, len' to socket `d' - */ - -static void -do_request(void *buf, size_t len, struct descr *d) -{ - krb5_error_code ret; - krb5_data reply; - - reply.length = 0; - - ret = process_request(buf, len, &reply, &d->peercred); - if (reply.length != 0) { - unsigned char len[4]; - struct msghdr msghdr; - struct iovec iov[2]; - - kcm_log(5, "sending %lu bytes to process %d", - (unsigned long)reply.length, - (int)d->peercred.pid); - - memset (&msghdr, 0, sizeof(msghdr)); - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - msghdr.msg_iov = iov; - msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); -#if 0 - msghdr.msg_control = NULL; - msghdr.msg_controllen = 0; -#endif - - len[0] = (reply.length >> 24) & 0xff; - len[1] = (reply.length >> 16) & 0xff; - len[2] = (reply.length >> 8) & 0xff; - len[3] = reply.length & 0xff; - - iov[0].iov_base = (void*)len; - iov[0].iov_len = 4; - iov[1].iov_base = reply.data; - iov[1].iov_len = reply.length; - - if (sendmsg (d->s, &msghdr, 0) < 0) { - kcm_log (0, "sendmsg(%d): %d %s", (int)d->peercred.pid, - errno, strerror(errno)); - krb5_data_free(&reply); - return; - } - - krb5_data_free(&reply); - } - - if (ret) { - kcm_log(0, "Failed processing %lu byte request from process %d", - (unsigned long)len, d->peercred.pid); - } -} - -static void -clear_descr(struct descr *d) -{ - if(d->buf) - memset(d->buf, 0, d->size); - d->len = 0; - if(d->s != -1) - close(d->s); - d->s = -1; -} - -#define STREAM_TIMEOUT 4 - -/* - * accept a new stream connection on `d[parent]' and store it in `d[child]' - */ - -static void -add_new_stream (struct descr *d, int parent, int child) -{ - int s; - - if (child == -1) - return; - - d[child].peercred.pid = -1; - d[child].peercred.uid = -1; - d[child].peercred.gid = -1; - - d[child].sock_len = sizeof(d[child].__ss); - s = accept(d[parent].s, d[child].sa, &d[child].sock_len); - if(s < 0) { - krb5_warn(kcm_context, errno, "accept"); - return; - } - - if (s >= FD_SETSIZE) { - krb5_warnx(kcm_context, "socket FD too large"); - close (s); - return; - } - - d[child].s = s; - d[child].timeout = time(NULL) + STREAM_TIMEOUT; - d[child].type = SOCK_STREAM; -} - -/* - * Grow `d' to handle at least `n'. - * Return != 0 if fails - */ - -static int -grow_descr (struct descr *d, size_t n) -{ - if (d->size - d->len < n) { - unsigned char *tmp; - size_t grow; - - grow = max(1024, d->len + n); - if (d->size + grow > max_request) { - kcm_log(0, "Request exceeds max request size (%lu bytes).", - (unsigned long)d->size + grow); - clear_descr(d); - return -1; - } - tmp = realloc (d->buf, d->size + grow); - if (tmp == NULL) { - kcm_log(0, "Failed to re-allocate %lu bytes.", - (unsigned long)d->size + grow); - clear_descr(d); - return -1; - } - d->size += grow; - d->buf = tmp; - } - return 0; -} - -/* - * Handle incoming data to the stream socket in `d[index]' - */ - -static void -handle_stream(struct descr *d, int index, int min_free) -{ - unsigned char buf[1024]; - int n; - int ret = 0; - - if (d[index].timeout == 0) { - add_new_stream (d, index, min_free); - return; - } - - if (update_client_creds(d[index].s, &d[index].peercred)) { - krb5_warnx(kcm_context, "failed to update peer identity"); - clear_descr(d + index); - return; - } - - if (d[index].peercred.uid == -1) { - krb5_warnx(kcm_context, "failed to determine peer identity"); - clear_descr (d + index); - return; - } - - n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL); - if (n < 0) { - krb5_warn(kcm_context, errno, "recvfrom"); - return; - } else if (n == 0) { - krb5_warnx(kcm_context, "connection closed before end of data " - "after %lu bytes from process %ld", - (unsigned long) d[index].len, (long) d[index].peercred.pid); - clear_descr (d + index); - return; - } - if (grow_descr (&d[index], n)) - return; - memcpy(d[index].buf + d[index].len, buf, n); - d[index].len += n; - if (d[index].len > 4) { - krb5_storage *sp; - int32_t len; - - sp = krb5_storage_from_mem(d[index].buf, d[index].len); - if (sp == NULL) { - kcm_log (0, "krb5_storage_from_mem failed"); - ret = -1; - } else { - krb5_ret_int32(sp, &len); - krb5_storage_free(sp); - if (d[index].len - 4 >= len) { - memmove(d[index].buf, d[index].buf + 4, d[index].len - 4); - ret = 1; - } else - ret = 0; - } - } - if (ret < 0) - return; - else if (ret == 1) { - do_request(d[index].buf, d[index].len, &d[index]); - clear_descr(d + index); - } -} - -#ifdef HAVE_DOOR_CREATE - -static void -kcm_door_server(void *cookie, char *argp, size_t arg_size, - door_desc_t *dp, uint_t n_desc) -{ - kcm_client peercred; - door_cred_t cred; - krb5_error_code ret; - krb5_data reply; - size_t length; - char *p; - - reply.length = 0; - - p = NULL; - length = 0; - - if (door_cred(&cred) != 0) { - kcm_log(0, "door_cred failed with %s", strerror(errno)); - goto out; - } - - peercred.uid = cred.dc_euid; - peercred.gid = cred.dc_egid; - peercred.pid = cred.dc_pid; - - ret = process_request((unsigned char*)argp, arg_size, &reply, &peercred); - if (reply.length != 0) { - p = alloca(reply.length); /* XXX don't use alloca */ - if (p) { - memcpy(p, reply.data, reply.length); - length = reply.length; - } - krb5_data_free(&reply); - } - - out: - door_return(p, length, NULL, 0); -} - -static void -kcm_setup_door(void) -{ - int fd, ret; - char *path; - - fd = door_create(kcm_door_server, NULL, 0); - if (fd < 0) - krb5_err(kcm_context, 1, errno, "Failed to create door"); - - if (door_path != NULL) - path = door_path; - else - path = _PATH_KCM_DOOR; - - unlink(path); - ret = open(path, O_RDWR | O_CREAT, 0666); - if (ret < 0) - krb5_err(kcm_context, 1, errno, "Failed to create/open door"); - close(ret); - - ret = fattach(fd, path); - if (ret < 0) - krb5_err(kcm_context, 1, errno, "Failed to attach door"); - -} -#endif /* HAVE_DOOR_CREATE */ - - void kcm_loop(void) { - struct descr *d; - unsigned int ndescr; + heim_sipc mach; + heim_sipc_launchd_mach_init("org.h5l.kcm", kcm_service, NULL, &mach); -#ifdef HAVE_DOOR_CREATE - kcm_setup_door(); -#endif - - ndescr = init_sockets(&d); - if (ndescr <= 0) { - krb5_warnx(kcm_context, "No sockets!"); -#ifndef HAVE_DOOR_CREATE - exit(1); -#endif - } - while (exit_flag == 0){ - struct timeval tmout; - fd_set fds; - int min_free = -1; - int max_fd = 0; - int i; - - FD_ZERO(&fds); - for(i = 0; i < ndescr; i++) { - if (d[i].s >= 0){ - if(d[i].type == SOCK_STREAM && - d[i].timeout && d[i].timeout < time(NULL)) { - kcm_log(1, "Stream connection from %d expired after %lu bytes", - d[i].peercred.pid, (unsigned long)d[i].len); - clear_descr(&d[i]); - continue; - } - if (max_fd < d[i].s) - max_fd = d[i].s; - if (max_fd >= FD_SETSIZE) - krb5_errx(kcm_context, 1, "fd too large"); - FD_SET(d[i].s, &fds); - } else if (min_free < 0 || i < min_free) - min_free = i; - } - if (min_free == -1) { - struct descr *tmp; - tmp = realloc(d, (ndescr + 4) * sizeof(*d)); - if(tmp == NULL) - krb5_warnx(kcm_context, "No memory"); - else { - d = tmp; - reinit_descrs (d, ndescr); - memset(d + ndescr, 0, 4 * sizeof(*d)); - for(i = ndescr; i < ndescr + 4; i++) - init_descr (&d[i]); - min_free = ndescr; - ndescr += 4; - } - } - - tmout.tv_sec = STREAM_TIMEOUT; - tmout.tv_usec = 0; - switch (select(max_fd + 1, &fds, 0, 0, &tmout)){ - case 0: - kcm_run_events(kcm_context, time(NULL)); - break; - case -1: - if (errno != EINTR) - krb5_warn(kcm_context, errno, "select"); - break; - default: - for(i = 0; i < ndescr; i++) { - if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) { - if (d[i].type == SOCK_STREAM) - handle_stream(d, i, min_free); - } - } - kcm_run_events(kcm_context, time(NULL)); - break; - } - } - if (d->path != NULL) - unlink(d->path); - free(d); + heim_ipc_main(); } diff --git a/kcm/events.c b/kcm/events.c index da4c220aa..e9c375f6a 100644 --- a/kcm/events.c +++ b/kcm/events.c @@ -161,7 +161,7 @@ kcm_remove_event_internal(krb5_context context, (*e)->fire_count = 0; (*e)->expire_time = 0; (*e)->backoff_time = 0; - kcm_release_ccache(context, &(*e)->ccache); + kcm_release_ccache(context, (*e)->ccache); (*e)->next = NULL; free(*e); diff --git a/kcm/headers.h b/kcm/headers.h index 714e02397..7c23d4212 100644 --- a/kcm/headers.h +++ b/kcm/headers.h @@ -33,9 +33,9 @@ #ifndef __HEADERS_H__ #define __HEADERS_H__ -#ifdef HAVE_CONFIG_H + #include -#endif + #include #include #include @@ -85,7 +85,9 @@ #include -#include +#include + +#include "crypto-headers.h" #endif /* __HEADERS_H__ */ diff --git a/kcm/kcm_locl.h b/kcm/kcm_locl.h index 41f82e454..8669ccc1d 100644 --- a/kcm/kcm_locl.h +++ b/kcm/kcm_locl.h @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -65,6 +67,15 @@ struct kcm_ccache_data; struct kcm_creds; +struct kcm_default_cache { + uid_t uid; + pid_t session; /* really au_asid_t */ + char *name; + struct kcm_default_cache *next; +}; + +extern struct kcm_default_cache *default_caches; + struct kcm_creds { kcmuuid_t uuid; krb5_creds cred; @@ -73,16 +84,19 @@ struct kcm_creds { typedef struct kcm_ccache_data { char *name; + kcmuuid_t uuid; unsigned refcnt; uint16_t flags; uint16_t mode; uid_t uid; gid_t gid; + pid_t session; /* really au_asid_t */ krb5_principal client; /* primary client principal */ krb5_principal server; /* primary server principal (TGS if NULL) */ struct kcm_creds *creds; krb5_deltat tkt_life; krb5_deltat renew_life; + int32_t kdc_offset; union { krb5_keytab keytab; krb5_keyblock keyblock; @@ -132,6 +146,7 @@ typedef struct kcm_client { pid_t pid; uid_t uid; gid_t gid; + pid_t session; } kcm_client; #define CLIENT_IS_ROOT(client) ((client)->uid == 0) diff --git a/kcm/protocol.c b/kcm/protocol.c index 362700767..de65599d8 100644 --- a/kcm/protocol.c +++ b/kcm/protocol.c @@ -2,6 +2,8 @@ * Copyright (c) 2005, PADL Software Pty Ltd. * All rights reserved. * + * Portions Copyright (c) 2009 Apple Inc. All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -31,8 +33,22 @@ */ #include "kcm_locl.h" +#include -RCSID("$Id$"); +static void +kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name); + + +int +kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session) +{ +#if 0 /* XXX pppd is running in diffrent session the user */ + if (session != -1) + return (client->session == session); + else +#endif + return (client->uid == uid); +} static krb5_error_code kcm_op_noop(krb5_context context, @@ -80,13 +96,13 @@ kcm_op_get_name(krb5_context context, ret = krb5_store_stringz(response, ccache->name); if (ret) { - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); free(name); return ret; } free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return 0; } @@ -181,7 +197,7 @@ kcm_op_initialize(krb5_context context, ret = kcm_enqueue_event_relative(context, &event); #endif - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -210,6 +226,8 @@ kcm_op_destroy(krb5_context context, KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_destroy_client(context, client, name); + if (ret == 0) + kcm_drop_default_cache(context, client, name); free(name); @@ -260,14 +278,14 @@ kcm_op_store(krb5_context context, if (ret) { free(name); krb5_free_cred_contents(context, &creds); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } kcm_ccache_enqueue_default(context, ccache, &creds); free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return 0; } @@ -334,7 +352,8 @@ kcm_op_retrieve(krb5_context context, ret = kcm_ccache_retrieve_cred(context, ccache, flags, &mcreds, &credp); - if (ret && ((flags & KRB5_GC_CACHED) == 0)) { + if (ret && ((flags & KRB5_GC_CACHED) == 0) && + !krb5_is_config_principal(context, mcreds.server)) { krb5_ccache_data ccdata; /* try and acquire */ @@ -357,7 +376,7 @@ kcm_op_retrieve(krb5_context context, free(name); krb5_free_cred_contents(context, &mcreds); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); if (free_creds) krb5_free_cred_contents(context, credp); @@ -402,7 +421,7 @@ kcm_op_get_principal(krb5_context context, ret = krb5_store_principal(response, ccache->client); free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return 0; } @@ -412,15 +431,15 @@ kcm_op_get_principal(krb5_context context, * NameZ * * Response: - * Cursor + * UUIDs * */ static krb5_error_code -kcm_op_get_first(krb5_context context, - kcm_client *client, - kcm_operation opcode, - krb5_storage *request, - krb5_storage *response) +kcm_op_get_cred_uuid_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) { struct kcm_creds *creds; krb5_error_code ret; @@ -448,7 +467,7 @@ kcm_op_get_first(krb5_context context, } } - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -462,11 +481,11 @@ kcm_op_get_first(krb5_context context, * Creds */ static krb5_error_code -kcm_op_get_next(krb5_context context, - kcm_client *client, - kcm_operation opcode, - krb5_storage *request, - krb5_storage *response) +kcm_op_get_cred_by_uuid(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; @@ -489,14 +508,14 @@ kcm_op_get_next(krb5_context context, sret = krb5_storage_read(request, &uuid, sizeof(uuid)); if (sret != sizeof(uuid)) { - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); krb5_clear_error_message(context); return KRB5_CC_IO; } c = kcm_ccache_find_cred_uuid(context, ccache, uuid); if (c == NULL) { - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return KRB5_CC_END; } @@ -504,35 +523,7 @@ kcm_op_get_next(krb5_context context, ret = krb5_store_creds(response, &c->cred); HEIMDAL_MUTEX_unlock(&ccache->mutex); - kcm_release_ccache(context, &ccache); - - return ret; -} - -/* - * Request: - * NameZ - * Cursor - * - * Response: - * - */ -static krb5_error_code -kcm_op_end_get(krb5_context context, - kcm_client *client, - kcm_operation opcode, - krb5_storage *request, - krb5_storage *response) -{ - krb5_error_code ret; - char *name; - - ret = krb5_ret_stringz(request, &name); - if (ret) - return ret; - - KCM_LOG_REQUEST_NAME(context, client, opcode, name); - free(name); + kcm_release_ccache(context, ccache); return ret; } @@ -591,7 +582,7 @@ kcm_op_remove_cred(krb5_context context, free(name); krb5_free_cred_contents(context, &mcreds); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -637,7 +628,7 @@ kcm_op_set_flags(krb5_context context, /* we don't really support any flags yet */ free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return 0; } @@ -692,7 +683,7 @@ kcm_op_chown(krb5_context context, ret = kcm_chown(context, client, ccache, uid, gid); free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -739,7 +730,7 @@ kcm_op_chmod(krb5_context context, ret = kcm_chmod(context, client, ccache, mode); free(name); - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -836,7 +827,7 @@ kcm_op_get_initial_ticket(krb5_context context, krb5_free_keyblock(context, &key); } - kcm_release_ccache(context, &ccache); + kcm_release_ccache(context, ccache); return ret; } @@ -915,9 +906,12 @@ kcm_op_get_ticket(krb5_context context, HEIMDAL_MUTEX_unlock(&ccache->mutex); + krb5_free_principal(context, server); + if (ret == 0) krb5_free_cred_contents(context, out); + kcm_release_ccache(context, ccache); free(name); return ret; @@ -969,7 +963,7 @@ kcm_op_move_cache(krb5_context context, if (ret) { free(oldname); - kcm_release_ccache(context, &oldid); + kcm_release_ccache(context, oldid); return ret; } @@ -989,23 +983,711 @@ kcm_op_move_cache(krb5_context context, MOVE(newid, oldid, tkt_life); MOVE(newid, oldid, renew_life); MOVE(newid, oldid, key); - MOVE(newid, oldid, key); + MOVE(newid, oldid, kdc_offset); #undef MOVE } HEIMDAL_MUTEX_unlock(&oldid->mutex); HEIMDAL_MUTEX_unlock(&newid->mutex); - kcm_release_ccache(context, &oldid); - kcm_release_ccache(context, &newid); + kcm_release_ccache(context, oldid); + kcm_release_ccache(context, newid); ret = kcm_ccache_destroy_client(context, client, oldname); + if (ret == 0) + kcm_drop_default_cache(context, client, oldname); free(oldname); return ret; } +static krb5_error_code +kcm_op_get_cache_uuid_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + KCM_LOG_REQUEST(context, client, opcode); + + return kcm_ccache_get_uuids(context, client, opcode, response); +} + +static krb5_error_code +kcm_op_get_cache_by_uuid(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcmuuid_t uuid; + ssize_t sret; + kcm_ccache cache; + + KCM_LOG_REQUEST(context, client, opcode); + + sret = krb5_storage_read(request, &uuid, sizeof(uuid)); + if (sret != sizeof(uuid)) { + krb5_clear_error_message(context); + return KRB5_CC_IO; + } + + ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache); + if (ret) + return ret; + + ret = kcm_access(context, client, opcode, cache); + if (ret) + ret = KRB5_FCC_NOFILE; + + if (ret == 0) + ret = krb5_store_stringz(response, cache->name); + + kcm_release_ccache(context, cache); + + return ret; +} + +struct kcm_default_cache *default_caches; + +static krb5_error_code +kcm_op_get_default_cache(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_default_cache *c; + krb5_error_code ret; + const char *name = NULL; + char *n = NULL; + + KCM_LOG_REQUEST(context, client, opcode); + + for (c = default_caches; c != NULL; c = c->next) { + if (kcm_is_same_session(client, c->uid, c->session)) { + name = c->name; + break; + } + } + if (name == NULL) + name = n = kcm_ccache_first_name(client); + + if (name == NULL) { + asprintf(&n, "%d", (int)client->uid); + name = n; + } + if (name == NULL) + return ENOMEM; + ret = krb5_store_stringz(response, name); + if (n) + free(n); + return ret; +} + +static void +kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) +{ + struct kcm_default_cache **c; + + for (c = &default_caches; *c != NULL; c = &(*c)->next) { + if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) + continue; + if (strcmp((*c)->name, name) == 0) { + struct kcm_default_cache *h = *c; + *c = (*c)->next; + free(h->name); + free(h); + break; + } + } +} + +static krb5_error_code +kcm_op_set_default_cache(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_default_cache *c; + krb5_error_code ret; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + for (c = default_caches; c != NULL; c = c->next) { + if (kcm_is_same_session(client, c->uid, c->session)) + break; + } + if (c == NULL) { + c = malloc(sizeof(*c)); + if (c == NULL) + return ENOMEM; + c->session = client->session; + c->uid = client->uid; + c->name = strdup(name); + + c->next = default_caches; + default_caches = c; + } else { + free(c->name); + c->name = strdup(name); + } + + return 0; +} + +static krb5_error_code +kcm_op_get_kdc_offset(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); + free(name); + if (ret) + return ret; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ret = krb5_store_int32(response, ccache->kdc_offset); + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + kcm_release_ccache(context, ccache); + + return ret; +} + +static krb5_error_code +kcm_op_set_kdc_offset(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + krb5_error_code ret; + kcm_ccache ccache; + int32_t offset; + char *name; + + ret = krb5_ret_stringz(request, &name); + if (ret) + return ret; + + KCM_LOG_REQUEST_NAME(context, client, opcode, name); + + ret = krb5_ret_int32(request, &offset); + if (ret) { + free(name); + return ret; + } + + ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); + free(name); + if (ret) + return ret; + + HEIMDAL_MUTEX_lock(&ccache->mutex); + ccache->kdc_offset = offset; + HEIMDAL_MUTEX_unlock(&ccache->mutex); + + kcm_release_ccache(context, ccache); + + return ret; +} + +struct kcm_ntlm_cred { + kcmuuid_t uuid; + char *user; + char *domain; + krb5_data nthash; + uid_t uid; + pid_t session; + struct kcm_ntlm_cred *next; +}; + +static struct kcm_ntlm_cred *ntlm_head; + +static void +free_cred(struct kcm_ntlm_cred *cred) +{ + free(cred->user); + free(cred->domain); + krb5_data_free(&cred->nthash); + free(cred); +} + + +/* + * name + * domain + * ntlm hash + * + * Reply: + * uuid + */ + +static struct kcm_ntlm_cred * +find_ntlm_cred(const char *user, const char *domain, kcm_client *client) +{ + struct kcm_ntlm_cred *c; + + for (c = ntlm_head; c != NULL; c = c->next) + if ((user[0] == '\0' || strcmp(user, c->user) == 0) && + (domain == NULL || strcmp(domain, c->domain) == 0) && + kcm_is_same_session(client, c->uid, c->session)) + return c; + + return NULL; +} + +static krb5_error_code +kcm_op_add_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *cred, *c; + krb5_error_code ret; + + cred = calloc(1, sizeof(*cred)); + if (cred == NULL) + return ENOMEM; + + RAND_bytes(cred->uuid, sizeof(cred->uuid)); + + ret = krb5_ret_stringz(request, &cred->user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &cred->domain); + if (ret) + goto error; + + ret = krb5_ret_data(request, &cred->nthash); + if (ret) + goto error; + + /* search for dups */ + c = find_ntlm_cred(cred->user, cred->domain, client); + if (c) { + krb5_data hash = c->nthash; + c->nthash = cred->nthash; + cred->nthash = hash; + free_cred(cred); + cred = c; + } else { + cred->next = ntlm_head; + ntlm_head = cred; + } + + cred->uid = client->uid; + cred->session = client->session; + + /* write response */ + (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); + + return 0; + + error: + free_cred(cred); + + return ret; +} + +/* + * { "HAVE_NTLM_CRED", NULL }, + * + * input: + * name + * domain + */ + +static krb5_error_code +kcm_op_have_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + char *user = NULL, *domain = NULL; + krb5_error_code ret; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + if (domain[0] == '\0') { + free(domain); + domain = NULL; + } + + c = find_ntlm_cred(user, domain, client); + if (c == NULL) + ret = ENOENT; + + error: + free(user); + if (domain) + free(domain); + + return ret; +} + +/* + * { "DEL_NTLM_CRED", NULL }, + * + * input: + * name + * domain + */ + +static krb5_error_code +kcm_op_del_ntlm_cred(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred **cp, *c; + char *user = NULL, *domain = NULL; + krb5_error_code ret; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { + if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 && + kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) + { + c = *cp; + *cp = c->next; + + free_cred(c); + break; + } + } + + error: + free(user); + free(domain); + + return ret; +} + +/* + * { "DO_NTLM_AUTH", NULL }, + * + * input: + * name:string + * domain:string + * type2:data + * + * reply: + * type3:data + * flags:int32 + * session-key:data + */ + +#define NTLM_FLAG_SESSIONKEY 1 +#define NTLM_FLAG_NTLM2_SESSION 2 +#define NTLM_FLAG_KEYEX 4 + +static krb5_error_code +kcm_op_do_ntlm(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + struct ntlm_type2 type2; + struct ntlm_type3 type3; + char *user = NULL, *domain = NULL; + struct ntlm_buf ndata, sessionkey; + krb5_data data; + krb5_error_code ret; + uint32_t flags = 0; + + memset(&type2, 0, sizeof(type2)); + memset(&type3, 0, sizeof(type3)); + sessionkey.data = NULL; + sessionkey.length = 0; + + ret = krb5_ret_stringz(request, &user); + if (ret) + goto error; + + ret = krb5_ret_stringz(request, &domain); + if (ret) + goto error; + + if (domain[0] == '\0') { + free(domain); + domain = NULL; + } + + c = find_ntlm_cred(user, domain, client); + if (c == NULL) { + ret = EINVAL; + goto error; + } + + ret = krb5_ret_data(request, &data); + if (ret) + goto error; + + ndata.data = data.data; + ndata.length = data.length; + + ret = heim_ntlm_decode_type2(&ndata, &type2); + krb5_data_free(&data); + if (ret) + goto error; + + if (domain && strcmp(domain, type2.targetname) == 0) { + ret = EINVAL; + goto error; + } + + type3.username = c->user; + type3.flags = type2.flags; + type3.targetname = type2.targetname; + type3.ws = rk_UNCONST("workstation"); + + /* + * NTLM Version 1 if no targetinfo buffer. + */ + + if (1 || type2.targetinfo.length == 0) { + struct ntlm_buf sessionkey; + + if (type2.flags & NTLM_NEG_NTLM2_SESSION) { + unsigned char nonce[8]; + + if (RAND_bytes(nonce, sizeof(nonce)) != 1) { + ret = EINVAL; + goto error; + } + + ret = heim_ntlm_calculate_ntlm2_sess(nonce, + type2.challange, + c->nthash.data, + &type3.lm, + &type3.ntlm); + } else { + ret = heim_ntlm_calculate_ntlm1(c->nthash.data, + c->nthash.length, + type2.challange, + &type3.ntlm); + + } + if (ret) + goto error; + + ret = heim_ntlm_build_ntlm1_master(c->nthash.data, + c->nthash.length, + &sessionkey, + &type3.sessionkey); + if (ret) { + if (type3.lm.data) + free(type3.lm.data); + if (type3.ntlm.data) + free(type3.ntlm.data); + goto error; + } + + free(sessionkey.data); + if (ret) { + if (type3.lm.data) + free(type3.lm.data); + if (type3.ntlm.data) + free(type3.ntlm.data); + goto error; + } + flags |= NTLM_FLAG_SESSIONKEY; +#if 0 + } else { + struct ntlm_buf sessionkey; + unsigned char ntlmv2[16]; + struct ntlm_targetinfo ti; + + /* verify infotarget */ + + ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); + if(ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, + ctx->client->key.length, + type3.username, + name->domain, + type2.challange, + &type2.targetinfo, + ntlmv2, + &type3.ntlm); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), + &sessionkey, + &type3.sessionkey); + memset(ntlmv2, 0, sizeof(ntlmv2)); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } + + flags |= NTLM_FLAG_NTLM2_SESSION | + NTLM_FLAG_SESSION; + + if (type3.flags & NTLM_NEG_KEYEX) + flags |= NTLM_FLAG_KEYEX; + + ret = krb5_data_copy(&ctx->sessionkey, + sessionkey.data, sessionkey.length); + free(sessionkey.data); + if (ret) { + _gss_ntlm_delete_sec_context(minor_status, + context_handle, NULL); + *minor_status = ret; + return GSS_S_FAILURE; + } +#endif + } + +#if 0 + if (flags & NTLM_FLAG_NTLM2_SESSION) { + _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), + ctx->sessionkey.data, + ctx->sessionkey.length); + _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), + ctx->sessionkey.data, + ctx->sessionkey.length); + } else { + flags |= NTLM_FLAG_SESSION; + RC4_set_key(&ctx->u.v1.crypto_recv.key, + ctx->sessionkey.length, + ctx->sessionkey.data); + RC4_set_key(&ctx->u.v1.crypto_send.key, + ctx->sessionkey.length, + ctx->sessionkey.data); + } +#endif + + ret = heim_ntlm_encode_type3(&type3, &ndata); + if (ret) + goto error; + + data.data = ndata.data; + data.length = ndata.length; + ret = krb5_store_data(response, data); + heim_ntlm_free_buf(&ndata); + if (ret) goto error; + + ret = krb5_store_int32(response, flags); + if (ret) goto error; + + data.data = sessionkey.data; + data.length = sessionkey.length; + + ret = krb5_store_data(response, data); + if (ret) goto error; + + error: + free(type3.username); + heim_ntlm_free_type2(&type2); + free(user); + if (domain) + free(domain); + + return ret; +} + + +/* + * { "GET_NTLM_UUID_LIST", NULL } + * + * reply: + * 1 user domain + * 0 [ end of list ] + */ + +static krb5_error_code +kcm_op_get_ntlm_user_list(krb5_context context, + kcm_client *client, + kcm_operation opcode, + krb5_storage *request, + krb5_storage *response) +{ + struct kcm_ntlm_cred *c; + krb5_error_code ret; + + for (c = ntlm_head; c != NULL; c = c->next) { + if (!kcm_is_same_session(client, c->uid, c->session)) + continue; + + ret = krb5_store_uint32(response, 1); + if (ret) + return ret; + ret = krb5_store_stringz(response, c->user); + if (ret) + return ret; + ret = krb5_store_stringz(response, c->domain); + if (ret) + return ret; + } + return krb5_store_uint32(response, 0); +} + +/* + * + */ static struct kcm_op kcm_ops[] = { { "NOOP", kcm_op_noop }, @@ -1017,20 +1699,31 @@ static struct kcm_op kcm_ops[] = { { "STORE", kcm_op_store }, { "RETRIEVE", kcm_op_retrieve }, { "GET_PRINCIPAL", kcm_op_get_principal }, - { "GET_FIRST", kcm_op_get_first }, - { "GET_NEXT", kcm_op_get_next }, - { "END_GET", kcm_op_end_get }, + { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, + { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, { "REMOVE_CRED", kcm_op_remove_cred }, { "SET_FLAGS", kcm_op_set_flags }, { "CHOWN", kcm_op_chown }, { "CHMOD", kcm_op_chmod }, { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, { "GET_TICKET", kcm_op_get_ticket }, - { "MOVE_CACHE", kcm_op_move_cache } + { "MOVE_CACHE", kcm_op_move_cache }, + { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, + { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, + { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, + { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, + { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, + { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, + { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, + { "HAVE_USER_CRED", kcm_op_have_ntlm_cred }, + { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred }, + { "DO_NTLM_AUTH", kcm_op_do_ntlm }, + { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list } }; -const char *kcm_op2string(kcm_operation opcode) +const char * +kcm_op2string(kcm_operation opcode) { if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) return "Unknown operation"; @@ -1082,6 +1775,12 @@ kcm_dispatch(krb5_context context, goto out; } method = kcm_ops[opcode].method; + if (method == NULL) { + kcm_log(0, "Process %d: operation code %s not implemented", + client->pid, kcm_op2string(opcode)); + ret = KRB5_FCC_INTERNAL; + goto out; + } /* seek past place for status code */ krb5_storage_seek(resp_sp, 4, SEEK_SET);