s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[amitay/samba.git] / source4 / heimdal / lib / krb5 / kcm.c
index f4372422ac88badb95d605a44eaf00f1a5e2a4f4..f0343419725f5aa0a8569f328ba478128f6a2537 100644 (file)
 
 #include "kcm.h"
 
-RCSID("$Id: kcm.c,v 1.8 2005/09/19 20:23:05 lha Exp $");
-
 typedef struct krb5_kcmcache {
     char *name;
     struct sockaddr_un path;
     char *door_path;
 } krb5_kcmcache;
 
+typedef struct krb5_kcm_cursor {
+    unsigned long offset;
+    unsigned long length;
+    kcmuuid_t *uuids;
+} *krb5_kcm_cursor;
+
+
 #define KCMCACHE(X)    ((krb5_kcmcache *)(X)->data.data)
 #define CACHENAME(X)   (KCMCACHE(X)->name)
-#define KCMCURSOR(C)   (*(u_int32_t *)(C))
+#define KCMCURSOR(C)   ((krb5_kcm_cursor)(C))
+
+#ifdef HAVE_DOOR_CREATE
 
 static krb5_error_code
-try_door(krb5_context context, const krb5_kcmcache *k,
+try_door(krb5_context context,
+        krb5_kcmcache *k,
         krb5_data *request_data,
         krb5_data *response_data)
 {
-#ifdef HAVE_DOOR_CREATE
     door_arg_t arg;
     int fd;
     int ret;
 
     memset(&arg, 0, sizeof(arg));
-          
+       
     fd = open(k->door_path, O_RDWR);
     if (fd < 0)
        return KRB5_CC_IO;
+    rk_cloexec(fd);
 
     arg.data_ptr = request_data->data;
     arg.data_size = request_data->length;
@@ -89,34 +97,34 @@ try_door(krb5_context context, const krb5_kcmcache *k,
        return ret;
 
     return 0;
-#else
-    return KRB5_CC_IO;
-#endif
 }
+#endif /* HAVE_DOOR_CREATE */
 
 static krb5_error_code
-try_unix_socket(krb5_context context, const krb5_kcmcache *k,
+try_unix_socket(krb5_context context,
+               krb5_kcmcache *k,
                krb5_data *request_data,
                krb5_data *response_data)
 {
     krb5_error_code ret;
     int fd;
 
-    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     if (fd < 0)
        return KRB5_CC_IO;
-    
+    rk_cloexec(fd);
+
     if (connect(fd, rk_UNCONST(&k->path), sizeof(k->path)) != 0) {
        close(fd);
        return KRB5_CC_IO;
     }
-    
+
     ret = _krb5_send_and_recv_tcp(fd, context->kdc_timeout,
                                  request_data, response_data);
     close(fd);
     return ret;
 }
-    
+
 static krb5_error_code
 kcm_send_request(krb5_context context,
                 krb5_kcmcache *k,
@@ -132,16 +140,18 @@ kcm_send_request(krb5_context context,
 
     ret = krb5_storage_to_data(request, &request_data);
     if (ret) {
-       krb5_clear_error_string(context);
+       krb5_clear_error_message(context);
        return KRB5_CC_NOMEM;
     }
 
-    ret = KRB5_CC_IO;
+    ret = KRB5_CC_NOSUPP;
 
     for (i = 0; i < context->max_retries; i++) {
+#ifdef HAVE_DOOR_CREATE
        ret = try_door(context, k, &request_data, response_data);
        if (ret == 0 && response_data->length != 0)
            break;
+#endif
        ret = try_unix_socket(context, k, &request_data, response_data);
        if (ret == 0 && response_data->length != 0)
            break;
@@ -150,8 +160,8 @@ kcm_send_request(krb5_context context,
     krb5_data_free(&request_data);
 
     if (ret) {
-       krb5_clear_error_string(context);
-       ret = KRB5_CC_IO;
+       krb5_clear_error_message(context);
+       ret = KRB5_CC_NOSUPP;
     }
 
     return ret;
@@ -169,7 +179,7 @@ kcm_storage_request(krb5_context context,
 
     sp = krb5_storage_emem();
     if (sp == NULL) {
-       krb5_set_error_string(context, "malloc: out of memory");
+       krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
        return KRB5_CC_NOMEM;
     }
 
@@ -187,11 +197,12 @@ kcm_storage_request(krb5_context context,
     *storage_p = sp;
  fail:
     if (ret) {
-       krb5_set_error_string(context, "Failed to encode request");
+       krb5_set_error_message(context, ret,
+                              N_("Failed to encode KCM request", ""));
        krb5_storage_free(sp);
     }
-   
-    return ret; 
+
+    return ret;
 }
 
 static krb5_error_code
@@ -202,7 +213,8 @@ kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
 
     k = malloc(sizeof(*k));
     if (k == NULL) {
-       krb5_set_error_string(context, "malloc: out of memory");
+       krb5_set_error_message(context, KRB5_CC_NOMEM,
+                              N_("malloc: out of memory", ""));
        return KRB5_CC_NOMEM;
     }
 
@@ -210,7 +222,8 @@ kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
        k->name = strdup(name);
        if (k->name == NULL) {
            free(k);
-           krb5_set_error_string(context, "malloc: out of memory");
+           krb5_set_error_message(context, KRB5_CC_NOMEM,
+                                  N_("malloc: out of memory", ""));
            return KRB5_CC_NOMEM;
        }
     } else
@@ -218,16 +231,16 @@ kcm_alloc(krb5_context context, const char *name, krb5_ccache *id)
 
     path = krb5_config_get_string_default(context, NULL,
                                          _PATH_KCM_SOCKET,
-                                         "libdefaults", 
+                                         "libdefaults",
                                          "kcm_socket",
                                          NULL);
-    
+
     k->path.sun_family = AF_UNIX;
     strlcpy(k->path.sun_path, path, sizeof(k->path.sun_path));
 
     path = krb5_config_get_string_default(context, NULL,
                                          _PATH_KCM_DOOR,
-                                         "libdefaults", 
+                                         "libdefaults",
                                          "kcm_door",
                                          NULL);
     k->door_path = strdup(path);
@@ -303,8 +316,6 @@ kcm_free(krb5_context context, krb5_ccache *id)
        memset(k, 0, sizeof(*k));
        krb5_data_free(&(*id)->data);
     }
-
-    *id = NULL;
 }
 
 static const char *
@@ -603,10 +614,10 @@ kcm_get_first (krb5_context context,
               krb5_cc_cursor *cursor)
 {
     krb5_error_code ret;
+    krb5_kcm_cursor c;
     krb5_kcmcache *k = KCMCACHE(id);
     krb5_storage *request, *response;
     krb5_data response_data;
-    int32_t tmp;
 
     ret = kcm_storage_request(context, KCM_OP_GET_FIRST, &request);
     if (ret)
@@ -619,27 +630,56 @@ kcm_get_first (krb5_context context,
     }
 
     ret = kcm_call(context, k, request, &response, &response_data);
-    if (ret) {
-       krb5_storage_free(request);
+    krb5_storage_free(request);
+    if (ret)
+       return ret;
+
+    c = calloc(1, sizeof(*c));
+    if (c == NULL) {
+       ret = ENOMEM;
+       krb5_set_error_message(context, ret, 
+                              N_("malloc: out of memory", ""));
        return ret;
     }
 
-    ret = krb5_ret_int32(response, &tmp);
-    if (ret || tmp < 0)
-       ret = KRB5_CC_IO;
+    while (1) {
+       ssize_t sret;
+       kcmuuid_t uuid;
+       void *ptr;
+
+       sret = krb5_storage_read(response, &uuid, sizeof(uuid));
+       if (sret == 0) {
+           ret = 0;
+           break;
+       } else if (sret != sizeof(uuid)) {
+           ret = EINVAL;
+           break;
+       }
+
+       ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
+       if (ptr == NULL) {
+           free(c->uuids);
+           free(c);
+           krb5_set_error_message(context, ENOMEM, 
+                                  N_("malloc: out of memory", ""));
+           return ENOMEM;
+       }
+       c->uuids = ptr;
+
+       memcpy(&c->uuids[c->length], &uuid, sizeof(uuid));
+       c->length += 1;
+    }
 
-    krb5_storage_free(request);
     krb5_storage_free(response);
     krb5_data_free(&response_data);
 
-    if (ret)
+    if (ret) {
+        free(c->uuids);
+        free(c);
        return ret;
+    }
 
-    *cursor = malloc(sizeof(tmp));
-    if (*cursor == NULL)
-       return KRB5_CC_NOMEM;
-
-    KCMCURSOR(*cursor) = tmp;
+    *cursor = c;
 
     return 0;
 }
@@ -660,8 +700,15 @@ kcm_get_next (krb5_context context,
 {
     krb5_error_code ret;
     krb5_kcmcache *k = KCMCACHE(id);
+    krb5_kcm_cursor c = KCMCURSOR(*cursor);
     krb5_storage *request, *response;
     krb5_data response_data;
+    ssize_t sret;
+
+ again:
+
+    if (c->offset >= c->length)
+       return KRB5_CC_END;
 
     ret = kcm_storage_request(context, KCM_OP_GET_NEXT, &request);
     if (ret)
@@ -673,23 +720,26 @@ kcm_get_next (krb5_context context,
        return ret;
     }
 
-    ret = krb5_store_int32(request, KCMCURSOR(*cursor));
-    if (ret) {
+    sret = krb5_storage_write(request, 
+                             &c->uuids[c->offset],
+                             sizeof(c->uuids[c->offset]));
+    c->offset++;
+    if (sret != sizeof(c->uuids[c->offset])) {
        krb5_storage_free(request);
-       return ret;
+       krb5_clear_error_message(context);
+       return ENOMEM;
     }
 
     ret = kcm_call(context, k, request, &response, &response_data);
-    if (ret) {
-       krb5_storage_free(request);
-       return ret;
+    krb5_storage_free(request);
+    if (ret == KRB5_CC_END) {
+       goto again;
     }
 
     ret = krb5_ret_creds(response, creds);
     if (ret)
        ret = KRB5_CC_IO;
 
-    krb5_storage_free(request);
     krb5_storage_free(response);
     krb5_data_free(&response_data);
 
@@ -711,6 +761,7 @@ kcm_end_get (krb5_context context,
 {
     krb5_error_code ret;
     krb5_kcmcache *k = KCMCACHE(id);
+    krb5_kcm_cursor c = KCMCURSOR(*cursor);
     krb5_storage *request;
 
     ret = kcm_storage_request(context, KCM_OP_END_GET, &request);
@@ -723,22 +774,14 @@ kcm_end_get (krb5_context context,
        return ret;
     }
 
-    ret = krb5_store_int32(request, KCMCURSOR(*cursor));
-    if (ret) {
-       krb5_storage_free(request);
-       return ret;
-    }
-
     ret = kcm_call(context, k, request, NULL, NULL);
-    if (ret) {
-       krb5_storage_free(request);
-       return ret;
-    }
-  
     krb5_storage_free(request);
+    if (ret)
+       return ret;
+
+    free(c->uuids);
+    free(c);
 
-    KCMCURSOR(*cursor) = 0;
-    free(*cursor);
     *cursor = NULL;
 
     return ret;
@@ -822,14 +865,65 @@ kcm_set_flags(krb5_context context,
     return ret;
 }
 
-static krb5_error_code
+static int
 kcm_get_version(krb5_context context,
                krb5_ccache id)
 {
     return 0;
 }
 
-const krb5_cc_ops krb5_kcm_ops = {
+static krb5_error_code
+kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+    krb5_error_code ret;
+    krb5_kcmcache *oldk = KCMCACHE(from);
+    krb5_kcmcache *newk = KCMCACHE(to);
+    krb5_storage *request;
+
+    ret = kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request);
+    if (ret)
+       return ret;
+
+    ret = krb5_store_stringz(request, oldk->name);
+    if (ret) {
+       krb5_storage_free(request);
+       return ret;
+    }
+
+    ret = krb5_store_stringz(request, newk->name);
+    if (ret) {
+       krb5_storage_free(request);
+       return ret;
+    }
+    ret = kcm_call(context, oldk, request, NULL, NULL);
+
+    krb5_storage_free(request);
+    return ret;
+}
+
+static krb5_error_code
+kcm_default_name(krb5_context context, char **str)
+{
+    return _krb5_expand_default_cc_name(context,
+                                       KRB5_DEFAULT_CCNAME_KCM,
+                                       str);
+}
+
+static krb5_error_code
+kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+    *mtime = time(NULL);
+    return 0;
+}
+
+/**
+ * Variable containing the KCM based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = {
+    KRB5_CC_OPS_VERSION,
     "KCM",
     kcm_get_name,
     kcm_resolve,
@@ -845,7 +939,14 @@ const krb5_cc_ops krb5_kcm_ops = {
     kcm_end_get,
     kcm_remove_cred,
     kcm_set_flags,
-    kcm_get_version
+    kcm_get_version,
+    NULL,
+    NULL,
+    NULL,
+    kcm_move,
+    kcm_default_name,
+    NULL,
+    kcm_lastchange
 };
 
 krb5_boolean
@@ -903,7 +1004,7 @@ _krb5_kcm_noop(krb5_context context,
 krb5_error_code
 _krb5_kcm_chmod(krb5_context context,
                krb5_ccache id,
-               u_int16_t mode)
+               uint16_t mode)
 {
     krb5_error_code ret;
     krb5_kcmcache *k = KCMCACHE(id);
@@ -944,8 +1045,8 @@ _krb5_kcm_chmod(krb5_context context,
 krb5_error_code
 _krb5_kcm_chown(krb5_context context,
                krb5_ccache id,
-               u_int32_t uid,
-               u_int32_t gid)
+               uint32_t uid,
+               uint32_t gid)
 {
     krb5_error_code ret;
     krb5_kcmcache *k = KCMCACHE(id);
@@ -1092,5 +1193,4 @@ _krb5_kcm_get_ticket(krb5_context context,
     return ret;
 }
 
-
 #endif /* HAVE_KCM */