add support for add,get,delete,chrand for the MIT kadmin protocol
authorLove Hörnquist Åstrand <lha@kth.se>
Sun, 11 Jan 2009 21:42:02 +0000 (21:42 +0000)
committerLove Hörnquist Åstrand <lha@kth.se>
Sun, 11 Jan 2009 21:42:02 +0000 (21:42 +0000)
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@24240 ec53bebd-3082-4978-b11e-865c3cabbd6b

kadmin/Makefile.am
kadmin/kadmin_locl.h
kadmin/kadmind.c
kadmin/rpc.c [new file with mode: 0644]
kadmin/server.c

index 4393c6c739631e16b3d14c9efe0627b45bd64c26..95efa28695933486b3901fcd7390837df8133116 100644 (file)
@@ -47,10 +47,10 @@ kadmin-commands.c kadmin-commands.h: kadmin-commands.in
        $(SLC) $(srcdir)/kadmin-commands.in
 
 kadmind_SOURCES =                              \
-       kadmind.c                               \
+       rpc.c                                   \
        server.c                                \
+       kadmind.c                               \
        kadmin_locl.h                           \
-       $(version4_c)                           \
        kadm_conn.c
 
 add_random_users_SOURCES = add-random-users.c
@@ -71,6 +71,7 @@ LDADD_common = \
        $(DBLIB)
 
 kadmind_LDADD = $(top_builddir)/lib/kadm5/libkadm5srv.la \
+       ../lib/gssapi/libgssapi.la \
        $(LDADD_common) \
        $(LIB_pidfile) \
        $(LIB_dlopen)
index 47ac5c62558f1f57abc3685b74329b639d07d31a..117fb563708e03ec1de2cf4bf8755c2fadaa5971 100644 (file)
@@ -132,11 +132,6 @@ foreach_principal(const char *, int (*)(krb5_principal, void*),
 
 int parse_des_key (const char *, krb5_key_data *, const char **);
 
-/* server.c */
-
-krb5_error_code
-kadmind_loop (krb5_context, krb5_auth_context, krb5_keytab, int);
-
 /* random_password.c */
 
 void
@@ -152,6 +147,12 @@ int start_server(krb5_context);
 /* server.c */
 
 krb5_error_code
-kadmind_loop (krb5_context, krb5_auth_context, krb5_keytab, int);
+kadmind_loop (krb5_context, krb5_keytab, int);
+
+/* rpc.c */
+
+int
+handle_mit(krb5_context, void *, size_t, int);
+
 
 #endif /* __ADMIN_LOCL_H__ */
index 3ce3fd863b9301d5cd7a01b953c04ddce51e0580..068f1576f01a76a3099ec9dfcf2ee52c61590a58 100644 (file)
@@ -158,30 +158,37 @@ main(int argc, char **argv)
     if (ret)
        krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
 
-    {
-       int fd = 0;
+    if(debug_flag) {
+       int debug_port;
+       
+       if(port_str == NULL)
+           debug_port = krb5_getportbyname (context, "kerberos-adm",
+                                            "tcp", 749);
+       else
+           debug_port = htons(atoi(port_str));
+       mini_inetd(debug_port);
+    } else {
        struct sockaddr_storage __ss;
        struct sockaddr *sa = (struct sockaddr *)&__ss;
        socklen_t sa_size = sizeof(__ss);
-       krb5_auth_context ac = NULL;
-       int debug_port;
 
-       if(debug_flag) {
-           if(port_str == NULL)
-               debug_port = krb5_getportbyname (context, "kerberos-adm",
-                                                "tcp", 749);
-           else
-               debug_port = htons(atoi(port_str));
-           mini_inetd(debug_port);
-       } else if(roken_getsockname(STDIN_FILENO, sa, &sa_size) < 0 &&
-                  errno == ENOTSOCK) {
+       /*
+        * Check if we are running inside inetd or not, if not, start
+        * our own server.
+        */
+       
+       if(roken_getsockname(STDIN_FILENO, sa, &sa_size) < 0 &&
+              errno == ENOTSOCK) {
            parse_ports(context, port_str ? port_str : "+");
            pidfile(NULL);
            start_server(context);
        }
-       if(realm)
-           krb5_set_default_realm(context, realm); /* XXX */
-       kadmind_loop(context, ac, keytab, fd);
     }
+    
+    if(realm)
+       krb5_set_default_realm(context, realm); /* XXX */
+
+    kadmind_loop(context, keytab, STDIN_FILENO);
+
     return 0;
 }
diff --git a/kadmin/rpc.c b/kadmin/rpc.c
new file mode 100644 (file)
index 0000000..2d45e76
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadmin_locl.h"
+
+#include <gssapi.h>
+
+#define CHECK(x)                                                       \
+       do {                                                            \
+               int __r;                                                \
+               if ((__r = (x))) {                                      \
+                       krb5_errx(dcontext, 1, "Failed (%d) on %s:%d",  \
+                           __r, __FUNCTION__, __LINE__);               \
+               }                                                       \
+       } while(0)
+
+static krb5_context dcontext;
+
+#define INSIST(x) CHECK(!(x))
+
+#define VERSION2 0x12345702
+
+#define RPC_VERSION 2
+#define KADM_SERVER 2112
+#define VVERSION 2
+#define FLAVOR_GSS 6
+#define FLAVOR_GSS_VERSION 1
+
+struct opaque_auth {
+    uint32_t flavor;
+    krb5_data data;
+};
+
+struct call_header {
+    uint32_t xid;
+    uint32_t rpcvers;
+    uint32_t prog;
+    uint32_t vers;
+    uint32_t proc;
+    struct opaque_auth cred;
+    struct opaque_auth verf;
+};
+
+struct reply_header {
+    uint32_t xid;
+    /* MSG_ACCEPTED = 0 */
+    /* struct opaque_auth */
+    /* SUCCESS = 0 */
+    krb5_data data;
+};
+
+enum {
+    RPG_DATA = 0,
+    RPG_INIT = 1,
+    RPG_CONTINUE_INIT = 2,
+    RPG_DESTROY = 3
+};
+
+enum {
+    rpg_privacy = 3
+};
+
+/*
+struct chrand_ret {
+       krb5_ui_4 api_version;
+       kadm5_ret_t ret;
+       int n_keys;
+       krb5_keyblock *keys;
+};
+*/
+
+
+struct gcred {
+    uint32_t version;
+    uint32_t proc;
+    uint32_t seq_num;
+    uint32_t service;
+    krb5_data handle;
+};
+
+static int
+parse_name(const unsigned char *p, size_t len,
+          const gss_OID oid, char **name)
+{
+    size_t l;
+    
+    if (len < 4)
+       return 1;
+    
+    /* TOK_ID */
+    if (memcmp(p, "\x04\x01", 2) != 0)
+       return 1;
+    len -= 2;
+    p += 2;
+    
+    /* MECH_LEN */
+    l = (p[0] << 8) | p[1];
+    len -= 2;
+    p += 2;
+    if (l < 2 || len < l)
+       return 1;
+    
+    /* oid wrapping */
+    if (p[0] != 6 || p[1] != l - 2)
+       return 1;
+    p += 2;
+    l -= 2;
+    len -= 2;
+    
+    /* MECH */
+    if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
+       return 1;
+    len -= l;
+    p += l;
+    
+    /* MECHNAME_LEN */
+    if (len < 4)
+       return 1;
+    l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+    len -= 4;
+    p += 4;
+    
+    /* MECH NAME */
+    if (len != l)
+       return 1;
+    
+    *name = malloc(l + 1);
+    INSIST(*name != NULL);
+    memcpy(*name, p, l);
+    (*name)[l] = '\0';
+
+    return 0;
+}
+
+
+
+static void
+gss_error(krb5_context context,
+         gss_OID mech, OM_uint32 type, OM_uint32 error)
+{
+    OM_uint32 new_stat;
+    OM_uint32 msg_ctx = 0;
+    gss_buffer_desc status_string;
+    OM_uint32 ret;
+
+    do {
+       ret = gss_display_status (&new_stat,
+                                 error,
+                                 type,
+                                 mech,
+                                 &msg_ctx,
+                                 &status_string);
+       krb5_warnx(context, "%.*s",
+                  (int)status_string.length,
+                  (char *)status_string.value);
+       gss_release_buffer (&new_stat, &status_string);
+    } while (!GSS_ERROR(ret) && msg_ctx != 0);
+}
+
+static void
+gss_print_errors (krb5_context context, 
+                 OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+    gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
+    gss_error(context, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
+    exit(1);
+}
+
+static int
+read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
+{
+    char buf[1024];
+
+    while (len) {
+       size_t tlen = len;
+       ssize_t slen;
+
+       if (tlen > sizeof(buf))
+           tlen = sizeof(buf);
+       
+       slen = krb5_storage_read(sp, buf, tlen);
+       INSIST(slen == tlen);
+       
+       slen = krb5_storage_write(msg, buf, tlen);
+       INSIST(slen == tlen);
+       
+       len -= tlen;
+    }
+    return 0;
+}
+
+static int
+collect_framents(krb5_storage *sp, krb5_storage *msg)
+{
+    krb5_error_code ret;
+    uint32_t len;
+    int last_fragment;
+    size_t total_len = 0;
+
+    do {
+       ret = krb5_ret_uint32(sp, &len);
+       if (ret)
+           return ret;
+       
+       krb5_warnx(dcontext, "collect len: %08x", (unsigned)len);
+
+       last_fragment = (len & 0x80000000) != 0;
+       len &= ~0x80000000;
+
+       CHECK(read_data(sp, msg, len));
+       total_len += len;
+
+    } while(last_fragment == 0 || total_len == 0);
+    krb5_storage_seek(msg, 0, SEEK_SET);
+
+    krb5_warnx(dcontext, "collect total_len: %08x", (unsigned)total_len);
+
+    return 0;
+}
+
+static krb5_error_code
+ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
+{
+    krb5_error_code ret;
+    ret = krb5_ret_uint32(msg, &ao->flavor);
+    if (ret) return ret;
+    ret = krb5_ret_data_xdr(msg, &ao->data);
+    return ret;
+}
+
+static int
+ret_gcred(krb5_data *data, struct gcred *gcred)
+{
+    krb5_storage *sp;
+
+    memset(gcred, 0, sizeof(*gcred));
+
+    sp = krb5_storage_from_data(data);
+    INSIST(sp != NULL);
+
+    CHECK(krb5_ret_uint32(sp, &gcred->version));
+    CHECK(krb5_ret_uint32(sp, &gcred->proc));
+    CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
+    CHECK(krb5_ret_uint32(sp, &gcred->service));
+    CHECK(krb5_ret_data_xdr(sp, &gcred->handle));
+
+    krb5_storage_free(sp);
+
+    return 0;
+}
+
+static krb5_error_code
+store_gss_init_res(krb5_storage *sp, krb5_data handle,
+                  OM_uint32 maj_stat, OM_uint32 min_stat,
+                  uint32_t seq_window, gss_buffer_t gout)
+{
+    krb5_error_code ret;
+    krb5_data out;
+
+    out.data = gout->value;
+    out.length = gout->length;
+
+    ret = krb5_store_data_xdr(sp, handle);
+    if (ret) return ret;
+    ret = krb5_store_uint32(sp, maj_stat);
+    if (ret) return ret;
+    ret = krb5_store_uint32(sp, min_stat);
+    if (ret) return ret;
+    ret = krb5_store_data_xdr(sp, out);
+    return ret;
+}
+
+static int
+store_string_xdr(krb5_storage *sp, const char *str)
+{
+    krb5_data c;
+    if (str) {
+       c.data = rk_UNCONST(str);
+       c.length = strlen(str) + 1;
+    } else
+       krb5_data_zero(&c);
+       
+    return krb5_store_data_xdr(sp, c);
+}
+
+static int
+ret_string_xdr(krb5_storage *sp, char **str)
+{
+    krb5_data c;
+    *str = NULL;
+    CHECK(krb5_ret_data_xdr(sp, &c));
+    if (c.length) {
+       *str = malloc(c.length + 1);
+       INSIST(*str != NULL);
+       memcpy(*str, c.data, c.length);
+       (*str)[c.length] = '\0';
+       krb5_data_free(&c);
+    }
+    return 0;
+}
+
+static int
+store_principal_xdr(krb5_context context,
+                   krb5_storage *sp,
+                   krb5_principal p)
+{
+    char *str;
+    CHECK(krb5_unparse_name(context, p, &str));
+    CHECK(store_string_xdr(sp, str));
+    free(str);
+    return 0;
+}
+
+static int
+ret_principal_xdr(krb5_context context,
+                 krb5_storage *sp,
+                 krb5_principal *p)
+{
+    char *str;
+    *p = NULL;
+    CHECK(ret_string_xdr(sp, &str));
+    if (str) {
+       CHECK(krb5_parse_name(context, str, p));
+       free(str);
+    }
+    return 0;
+}
+
+static int
+store_principal_ent(krb5_context context,
+                   krb5_storage *sp,
+                   kadm5_principal_ent_rec *ent)
+{
+    size_t i;
+
+    CHECK(store_principal_xdr(context, sp, ent->principal));
+    CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
+    CHECK(krb5_store_uint32(sp, ent->pw_expiration));
+    CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
+    CHECK(krb5_store_uint32(sp, ent->max_life));
+    CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
+    if (ent->mod_name)
+       CHECK(store_principal_xdr(context, sp, ent->mod_name));
+    CHECK(krb5_store_uint32(sp, ent->mod_date));
+    CHECK(krb5_store_uint32(sp, ent->attributes));
+    CHECK(krb5_store_uint32(sp, ent->kvno));
+    CHECK(krb5_store_uint32(sp, ent->mkvno));
+    CHECK(store_string_xdr(sp, ent->policy));
+    CHECK(krb5_store_int32(sp, ent->aux_attributes));
+    CHECK(krb5_store_int32(sp, ent->max_renewable_life));
+    CHECK(krb5_store_int32(sp, ent->last_success));
+    CHECK(krb5_store_int32(sp, ent->last_failed));
+    CHECK(krb5_store_int32(sp, ent->fail_auth_count));
+    CHECK(krb5_store_int32(sp, ent->n_key_data));
+    CHECK(krb5_store_int32(sp, ent->n_tl_data));
+    CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
+    if (ent->n_tl_data) {
+       krb5_tl_data *tp;
+
+       for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
+           krb5_data c;
+           c.length = tp->tl_data_length;
+           c.data = tp->tl_data_contents;
+
+           CHECK(krb5_store_int32(sp, 0)); /* more */
+           CHECK(krb5_store_int32(sp, tp->tl_data_type));
+           CHECK(krb5_store_data_xdr(sp, c));
+       }
+       CHECK(krb5_store_int32(sp, 1)); /* more */
+    }
+
+    CHECK(krb5_store_int32(sp, ent->n_key_data));
+    for (i = 0; i < ent->n_key_data; i++) {
+       CHECK(krb5_store_uint32(sp, 2));
+       CHECK(krb5_store_uint32(sp, ent->kvno));
+       CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
+       CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
+    }
+
+    return 0;
+}
+
+static int
+ret_principal_ent(krb5_context context,
+                 krb5_storage *sp,
+                 kadm5_principal_ent_rec *ent)
+{
+    uint32_t flag, num;
+    size_t i;
+
+    memset(ent, 0, sizeof(*ent));
+
+    CHECK(ret_principal_xdr(context, sp, &ent->principal));
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->princ_expire_time = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->pw_expiration = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->last_pwd_change = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->max_life = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    if (flag == 0)
+       ret_principal_xdr(context, sp, &ent->mod_name);
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->mod_date = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->attributes = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->kvno = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->mkvno = flag;
+    CHECK(ret_string_xdr(sp, &ent->policy));
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->aux_attributes = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->max_renewable_life = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->last_success = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->last_failed = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->fail_auth_count = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->n_key_data = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    ent->n_tl_data = flag;
+    CHECK(krb5_ret_uint32(sp, &flag));
+    if (flag == 0) {
+       //      krb5_tl_data *tp;
+
+       while(1) {
+           krb5_data c;
+           CHECK(krb5_ret_uint32(sp, &flag));
+           if (flag)
+               break;
+           CHECK(krb5_ret_uint32(sp, &num));
+           CHECK(krb5_ret_data_xdr(sp, &c));
+           krb5_data_free(&c);
+       }
+    }
+         
+    CHECK(krb5_ret_uint32(sp, &num));
+    INSIST(num == ent->n_key_data);
+
+    ent->key_data = calloc(num, sizeof(ent->key_data[0]));
+    INSIST(ent->key_data != NULL);
+
+    for (i = 0; i < num; i++) {
+       CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
+       INSIST(flag > 1);
+       CHECK(krb5_ret_uint32(sp, &flag));
+       ent->kvno = flag;
+       CHECK(krb5_ret_uint32(sp, &flag));
+       ent->key_data[i].key_data_type[0] = flag;
+       CHECK(krb5_ret_uint32(sp, &flag));
+       ent->key_data[i].key_data_type[1] = flag;
+    }
+
+    return 0;
+}
+
+/*
+ *
+ */
+
+static void
+proc_create_principal(kadm5_server_context *context,
+                     krb5_storage *in,
+                     krb5_storage *out)
+{
+    uint32_t version, mask;
+    kadm5_principal_ent_rec ent;
+    krb5_error_code ret;
+    char *password;
+
+    memset(&ent, 0, sizeof(ent));
+
+    CHECK(krb5_ret_uint32(in, &version));
+    INSIST(version == VERSION2);
+    CHECK(ret_principal_ent(context->context, in, &ent));
+    CHECK(krb5_ret_uint32(in, &mask));
+    CHECK(ret_string_xdr(in, &password));
+
+    INSIST(ent.principal);
+
+
+    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD, ent.principal);
+    if (ret)
+       goto fail;
+
+    ret = kadm5_create_principal(context, &ent, mask, password);
+
+ fail:
+    krb5_warn(context->context, ret, "create principal");
+    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+    CHECK(krb5_store_uint32(out, ret)); /* code */
+
+    free(password);
+    kadm5_free_principal_ent(context, &ent);
+}
+
+static void
+proc_delete_principal(kadm5_server_context *context,
+                     krb5_storage *in,
+                     krb5_storage *out)
+{
+    uint32_t version;
+    krb5_principal princ;
+    krb5_error_code ret;
+
+    CHECK(krb5_ret_uint32(in, &version));
+    INSIST(version == VERSION2);
+    CHECK(ret_principal_xdr(context->context, in, &princ));
+
+    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ);
+    if (ret)
+       goto fail;
+
+    ret = kadm5_delete_principal(context, princ);
+
+ fail:
+    krb5_warn(context->context, ret, "delete principal");
+    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+    CHECK(krb5_store_uint32(out, ret)); /* code */
+
+    krb5_free_principal(context->context, princ);
+}
+
+static void
+proc_get_principal(kadm5_server_context *context,
+                  krb5_storage *in,
+                  krb5_storage *out)
+{
+    uint32_t version, mask;
+    krb5_principal princ;
+    kadm5_principal_ent_rec ent;
+    krb5_error_code ret;
+
+    memset(&ent, 0, sizeof(ent));
+
+    CHECK(krb5_ret_uint32(in, &version));
+    INSIST(version == VERSION2);
+    CHECK(ret_principal_xdr(context->context, in, &princ));
+    CHECK(krb5_ret_uint32(in, &mask));
+
+    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ);
+    if(ret)
+       goto fail;
+
+    ret = kadm5_get_principal(context, princ, &ent, mask);
+
+ fail:
+    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+    CHECK(krb5_store_uint32(out, ret)); /* code */
+    if (ret == 0) {
+       CHECK(store_principal_ent(context->context, out, &ent));
+    }
+    krb5_free_principal(context->context, princ);
+    kadm5_free_principal_ent(context, &ent);
+}
+
+static void
+proc_chrand_principal_v2(kadm5_server_context *context,
+                        krb5_storage *in, 
+                        krb5_storage *out)
+{
+    krb5_error_code ret;
+    krb5_principal princ;
+    uint32_t version;
+    krb5_keyblock *new_keys;
+    int n_keys;
+
+    CHECK(krb5_ret_uint32(in, &version));
+    INSIST(version == VERSION2);
+    CHECK(ret_principal_xdr(context->context, in, &princ));
+
+    ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
+    if(ret)
+       goto fail;
+
+    ret = kadm5_randkey_principal(context, princ,
+                                 &new_keys, &n_keys);
+
+ fail:
+    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+    CHECK(krb5_store_uint32(out, ret));
+    if (ret == 0) {
+       size_t i;
+       CHECK(krb5_store_int32(out, n_keys));
+
+       for(i = 0; i < n_keys; i++){
+           CHECK(krb5_store_uint32(out, new_keys[i].keytype));
+           CHECK(krb5_store_data_xdr(out, new_keys[i].keyvalue));
+           krb5_free_keyblock_contents(context->context, &new_keys[i]);
+       }
+       free(new_keys);
+    }
+    krb5_free_principal(context->context, princ);
+}
+
+static void
+proc_init(kadm5_server_context *context,
+         krb5_storage *in,
+         krb5_storage *out)
+{
+    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+    CHECK(krb5_store_uint32(out, 0)); /* code */
+}
+
+struct proc {
+    char *name;
+    void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
+} procs[] = {
+    { "NULL", NULL },
+    { "create principal", proc_create_principal },
+    { "delete principal", proc_delete_principal },
+    { "modify principal", NULL },
+    { "rename principal", NULL },
+    { "get principal", proc_get_principal },
+    { "chpass principal", NULL },
+    { "chrand principal", proc_chrand_principal_v2 },
+    { "create policy", NULL },
+    { "delete policy", NULL },
+    { "modify policy", NULL },
+    { "get policy", NULL },
+    { "get privs", NULL },
+    { "init", proc_init },
+    { "get principals", NULL },
+    { "get polices", NULL },
+    { "setkey principal", NULL },
+    { "setkey principal v4", NULL },
+    { "create principal v3", NULL },
+    { "chpass principal v3", NULL },
+    { "chrand principal v3", NULL },
+    { "setkey principal v3", NULL }
+};
+
+struct gctx {
+    krb5_data handle;
+    gss_ctx_id_t ctx;
+    uint32_t seq_num;
+    int done;
+    int inprogress;
+};
+
+static int
+process_stream(krb5_context context, 
+              unsigned char *buf, size_t ilen,
+              krb5_storage *sp)
+{
+    krb5_error_code ret;
+    krb5_storage *msg, *reply, *dreply;
+    OM_uint32 maj_stat, min_stat;
+    gss_buffer_desc gin, gout;
+    struct gctx gctx;
+    void *server_handle = NULL;
+
+    memset(&gctx, 0, sizeof(gctx));
+
+    msg = krb5_storage_emem();
+    reply = krb5_storage_emem();
+    dreply = krb5_storage_emem();
+
+    /*
+     * First packet comes partly from the caller
+     */
+
+    INSIST(ilen >= 4);
+
+    while (1) {
+       struct call_header chdr;
+       struct gcred gcred;
+       uint32_t mtype;
+
+       krb5_storage_truncate(dreply, 0);
+       krb5_storage_truncate(reply, 0);
+       krb5_storage_truncate(msg, 0);
+       krb5_storage_seek(msg, 0, SEEK_SET);
+
+       if (ilen) {
+           int last_fragment;
+           unsigned long len;
+           ssize_t slen;
+           unsigned char tmp[4];
+
+           krb5_warnx(dcontext, "ilen %d", (int)ilen);
+
+           if (ilen < 4) {
+               memcpy(tmp, buf, ilen);
+               slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
+               INSIST(slen == sizeof(tmp) - ilen);
+
+               ilen = sizeof(tmp);
+               buf = tmp;
+           }
+           INSIST(ilen >= 4);
+           
+           _krb5_get_int(buf, &len, 4);
+           last_fragment = (len & 0x80000000) != 0;
+           len &= ~0x80000000;
+           
+           ilen -= 4;
+           buf += 4;
+
+           if (ilen) {
+               if (len < ilen) {
+                   slen = krb5_storage_write(msg, buf, len);
+                   INSIST(slen == len);
+                   ilen -= len;
+                   len = 0;
+               } else {
+                   slen = krb5_storage_write(msg, buf, ilen);
+                   INSIST(slen == ilen);
+                   len -= ilen;
+               }
+           }
+
+           CHECK(read_data(sp, msg, len));
+           
+           krb5_warnx(dcontext, "len %d", (int)len);
+
+           if (last_fragment == 0) {
+               ret = collect_framents(sp, msg);
+               if (ret == HEIM_ERR_EOF)
+                   krb5_errx(context, 1, "client disconnected");
+               INSIST(ret == 0);
+           }
+       } else {
+
+           krb5_warnx(dcontext, "collect");
+           ret = collect_framents(sp, msg);
+           if (ret == HEIM_ERR_EOF)
+               krb5_errx(context, 1, "client disconnected");
+           INSIST(ret == 0);
+       }
+       krb5_storage_seek(msg, 0, SEEK_SET);
+
+       krb5_warnx(context, "msg size: %d", 
+                  (int)krb5_storage_seek(msg, 0, SEEK_END));
+       krb5_storage_seek(msg, 0, SEEK_SET);
+
+       /* 
+        * If context is setup, priv data have the seq_num stored
+        * first in the block, so add it here before users data is
+        * added.
+        */
+       if (gctx.done && gctx.inprogress == 0)
+           krb5_store_uint32(dreply, gctx.seq_num);
+
+       CHECK(krb5_ret_uint32(msg, &chdr.xid));
+       CHECK(krb5_ret_uint32(msg, &mtype));
+       CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
+       CHECK(krb5_ret_uint32(msg, &chdr.prog));
+       CHECK(krb5_ret_uint32(msg, &chdr.vers));
+       CHECK(krb5_ret_uint32(msg, &chdr.proc));
+       CHECK(ret_auth_opaque(msg, &chdr.cred));
+       CHECK(ret_auth_opaque(msg, &chdr.verf));
+
+       INSIST(chdr.rpcvers == RPC_VERSION);
+       INSIST(chdr.prog == KADM_SERVER);
+       INSIST(chdr.vers == VVERSION);
+       INSIST(chdr.cred.flavor == FLAVOR_GSS);
+
+       CHECK(ret_gcred(&chdr.cred.data, &gcred));
+       INSIST(gcred.version == FLAVOR_GSS_VERSION);
+
+       switch(gcred.proc) {
+       case RPG_DATA: {
+           krb5_data data;
+           int conf_state;
+           uint32_t seq;
+           krb5_storage *sp;
+
+           INSIST(gctx.done);
+
+           INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
+           
+           CHECK(krb5_ret_data_xdr(msg, &data));
+
+           gin.value = data.data;
+           gin.length = data.length;
+
+           maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
+                                 &conf_state, NULL);
+           krb5_data_free(&data);
+           INSIST(maj_stat == GSS_S_COMPLETE);
+           INSIST(conf_state != 0);
+
+           sp = krb5_storage_from_mem(gout.value, gout.length);
+           INSIST(sp != NULL);
+
+           CHECK(krb5_ret_uint32(sp, &seq));
+           INSIST(seq == gctx.seq_num);
+
+           if (chdr.proc < sizeof(procs[0])/sizeof(procs)) {
+               krb5_warnx(context, "proc number out of array");
+           } else if (procs[chdr.proc].func == NULL) {
+               krb5_warnx(context, "proc '%s' never implemented", 
+                         procs[chdr.proc].name);
+           } else {
+               krb5_warnx(context, "proc %s", procs[chdr.proc].name);
+               INSIST(server_handle != NULL);
+               (*procs[chdr.proc].func)(server_handle, sp, dreply);
+           }
+           krb5_storage_free(sp);
+           gss_release_buffer(&min_stat, &gout);
+
+           break;
+       }
+       case RPG_INIT:
+           gctx.inprogress = 1;
+           INSIST(gctx.ctx == NULL);
+           /* FALL THOUGH */
+       case RPG_CONTINUE_INIT: {
+           gss_name_t src_name = GSS_C_NO_NAME;
+           krb5_data in;
+
+           CHECK(krb5_ret_data_xdr(msg, &in));
+
+           gin.value = in.data;
+           gin.length = in.length;
+           gout.value = NULL;
+
+           maj_stat = gss_accept_sec_context(&min_stat,
+                                             &gctx.ctx, 
+                                             GSS_C_NO_CREDENTIAL,
+                                             &gin,
+                                             GSS_C_NO_CHANNEL_BINDINGS,
+                                             &src_name,
+                                             NULL,
+                                             &gout,
+                                             NULL,
+                                             NULL,
+                                             NULL);
+           if (GSS_ERROR(maj_stat))
+               gss_print_errors(context, maj_stat, min_stat);
+           if (maj_stat & GSS_S_CONTINUE_NEEDED)
+               ;
+           else {
+               kadm5_config_params realm_params;
+               gss_buffer_desc buf;
+               char *client;
+
+               gctx.done = 1;
+               
+               memset(&realm_params, 0, sizeof(realm_params));
+
+               maj_stat = gss_export_name(&min_stat, src_name, &buf);
+               INSIST(maj_stat == GSS_S_COMPLETE);
+
+               CHECK(parse_name(buf.value, buf.length, 
+                                GSS_KRB5_MECHANISM, &client));
+
+               gss_release_buffer(&min_stat, &buf);
+
+               krb5_warnx(context, "%s connected", client);
+
+               ret = kadm5_init_with_password_ctx(context,
+                                                  client,
+                                                  NULL,
+                                                  KADM5_ADMIN_SERVICE,
+                                                  &realm_params,
+                                                  0, 0,
+                                                  &server_handle);
+               INSIST(ret == 0);
+           }
+
+           CHECK(krb5_store_uint32(dreply, 0));  /* error code */
+           CHECK(store_gss_init_res(dreply, gctx.handle, 
+                                    maj_stat, min_stat, 1, &gout));
+           if (gout.value)
+               gss_release_buffer(&min_stat, &gout);
+           if (src_name)
+               gss_release_name(&min_stat, &src_name);
+
+           break;
+       }
+       case RPG_DESTROY:
+           krb5_errx(context, 1, "client destroyed gss context");
+       default:
+           krb5_errx(context, 1, "client sent unknown gsscode %d", 
+                     (int)gcred.proc);
+       }
+
+       krb5_data_free(&gcred.handle);
+       krb5_data_free(&chdr.cred.data);
+       krb5_data_free(&chdr.verf.data);
+
+       CHECK(krb5_store_uint32(reply, chdr.xid));
+       CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
+       CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
+       if (!gctx.done) {
+           krb5_data data;
+
+           CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
+           CHECK(krb5_store_uint32(reply, 0)); /* length */
+
+           CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
+
+           CHECK(krb5_storage_to_data(dreply, &data));
+           INSIST(krb5_storage_write(reply, data.data, data.length) == data.length);
+           krb5_data_free(&data);
+
+       } else {
+           uint32_t seqnum = htonl(gctx.seq_num);
+           krb5_data data;
+
+           gctx.seq_num++;
+
+           gin.value = &seqnum;
+           gin.length = sizeof(seqnum);
+
+           maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
+           INSIST(maj_stat == GSS_S_COMPLETE);
+
+           data.data = gout.value;
+           data.length = gout.length;
+
+           CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
+           CHECK(krb5_store_data_xdr(reply, data));
+           gss_release_buffer(&min_stat, &gout);
+
+           CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
+
+           krb5_storage_to_data(dreply, &data);
+
+           if (gctx.inprogress) {
+               gctx.inprogress = 0;
+               krb5_storage_write(reply, data.data, data.length);
+               krb5_data_free(&data);
+           } else {
+               int conf_state;
+
+               gin.value = data.data;
+               gin.length = data.length;
+               
+               maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
+                                   &gin, &conf_state, &gout);
+               INSIST(maj_stat == GSS_S_COMPLETE);
+               INSIST(conf_state != 0);
+               krb5_data_free(&data);
+               
+               data.data = gout.value;
+               data.length = gout.length;
+               
+               krb5_store_data_xdr(reply, data);
+               gss_release_buffer(&min_stat, &gout);
+           }
+       }
+
+       {
+           krb5_data data;
+           CHECK(krb5_storage_to_data(reply, &data));
+           if (data.length) {
+               CHECK(krb5_store_uint32(sp, data.length | 0x80000000));
+               INSIST(krb5_storage_write(sp, data.data, data.length) == data.length);
+               krb5_data_free(&data);
+           }
+       }
+
+    }
+}
+
+
+int
+handle_mit(krb5_context context, void *buf, size_t len, int fd)
+{
+    krb5_storage *sp;
+
+    dcontext = context;
+
+    sp = krb5_storage_from_fd(fd);
+    INSIST(sp != NULL);
+    
+    process_stream(context, buf, len, sp);
+    
+    return 0;
+}
index 16914f022e9cd225b0e50b7a314aa065d4e33176..13bdc2ad8829fd8ca45d9c079b78929ab259a24c 100644 (file)
@@ -367,6 +367,7 @@ kadmind_dispatch(void *kadm_handle, krb5_boolean initial,
                krb5_store_keyblock(sp, new_keys[i]);
                krb5_free_keyblock_contents(context->context, &new_keys[i]);
            }
+           free(new_keys);
        }
        break;
     }
@@ -471,33 +472,20 @@ match_appl_version(const void *data, const char *appl_version)
 
 static void
 handle_v5(krb5_context context,
-         krb5_auth_context ac,
          krb5_keytab keytab,
-         int len,
          int fd)
 {
     krb5_error_code ret;
-    u_char version[sizeof(KRB5_SENDAUTH_VERSION)];
     krb5_ticket *ticket;
     char *server_name;
     char *client;
     void *kadm_handle;
-    ssize_t n;
     krb5_boolean initial;
+    krb5_auth_context ac = NULL;
 
     unsigned kadm_version;
     kadm5_config_params realm_params;
 
-    if (len != sizeof(KRB5_SENDAUTH_VERSION))
-       krb5_errx(context, 1, "bad sendauth len %d", len);
-    n = krb5_net_read(context, &fd, version, len);
-    if (n < 0)
-       krb5_err (context, 1, errno, "reading sendauth version");
-    if (n == 0)
-       krb5_errx (context, 1, "EOF reading sendauth version");
-    if(memcmp(version, KRB5_SENDAUTH_VERSION, len) != 0)
-       krb5_errx(context, 1, "bad sendauth version %.8s", version);
-       
     ret = krb5_recvauth_match_version(context, &ac, &fd,
                                      match_appl_version, &kadm_version,
                                      NULL, KRB5_RECVAUTH_IGNORE_VERSION,
@@ -547,31 +535,37 @@ handle_v5(krb5_context context,
 
 krb5_error_code
 kadmind_loop(krb5_context context,
-            krb5_auth_context ac,
             krb5_keytab keytab,
             int fd)
 {
-    unsigned char tmp[4];
+    u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
     ssize_t n;
     unsigned long len;
 
-    n = krb5_net_read(context, &fd, tmp, 4);
+    n = krb5_net_read(context, &fd, buf, 4);
     if(n == 0)
        exit(0);
     if(n < 0)
        krb5_err(context, 1, errno, "read");
-    _krb5_get_int(tmp, &len, 4);
-    /* this v4 test could probably also go away */
-    if(len > 0xffff && (len & 0xffff) == ('K' << 8) + 'A') {
-       unsigned char v4reply[] = {
-           0x00, 0x0c,
-           'K', 'Y', 'O', 'U', 'L', 'O', 'S', 'E',
-           0x95, 0xb7, 0xa7, 0x08 /* KADM_BAD_VER */
-       };
-       krb5_net_write(context, &fd, v4reply, sizeof(v4reply));
-       krb5_errx(context, 1, "packet appears to be version 4");
-    } else {
-       handle_v5(context, ac, keytab, len, fd);
-    }
+    _krb5_get_int(buf, &len, 4);
+
+    if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
+
+       n = krb5_net_read(context, &fd, buf + 4, len);
+       if (n < 0)
+           krb5_err (context, 1, errno, "reading sendauth version");
+       if (n == 0)
+           krb5_errx (context, 1, "EOF reading sendauth version");
+
+       if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
+           handle_v5(context, keytab, fd);
+           return 0;
+       }
+       len += 4;
+    } else
+       len = 4;
+
+    handle_mit(context, buf, len, fd);
+
     return 0;
 }