Home | History | Annotate | Line # | Download | only in kadmin
      1 /*	$NetBSD: rpc.c,v 1.4 2023/06/19 21:41:41 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2008 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  *
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * 3. Neither the name of the Institute nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include "kadmin_locl.h"
     37 
     38 #include <gssapi/gssapi.h>
     39 #include <gssapi/gssapi_krb5.h>
     40 #include <gssapi/gssapi_spnego.h>
     41 
     42 #define CHECK(x)							\
     43 	do {								\
     44 		int __r;						\
     45 		if ((__r = (x))) {					\
     46 			krb5_errx(dcontext, 1, "Failed (%d) on %s:%d",	\
     47 			    __r, __FILE__, __LINE__);			\
     48 		}							\
     49 	} while(0)
     50 
     51 static krb5_context dcontext;
     52 
     53 #define INSIST(x) CHECK(!(x))
     54 
     55 #define VERSION2 0x12345702
     56 
     57 #define LAST_FRAGMENT 0x80000000
     58 
     59 #define RPC_VERSION 2
     60 #define KADM_SERVER 2112
     61 #define VVERSION 2
     62 #define FLAVOR_GSS 6
     63 #define FLAVOR_GSS_VERSION 1
     64 
     65 struct opaque_auth {
     66     uint32_t flavor;
     67     krb5_data data;
     68 };
     69 
     70 struct call_header {
     71     uint32_t xid;
     72     uint32_t rpcvers;
     73     uint32_t prog;
     74     uint32_t vers;
     75     uint32_t proc;
     76     struct opaque_auth cred;
     77     struct opaque_auth verf;
     78 };
     79 
     80 enum {
     81     RPG_DATA = 0,
     82     RPG_INIT = 1,
     83     RPG_CONTINUE_INIT = 2,
     84     RPG_DESTROY = 3
     85 };
     86 
     87 enum {
     88     rpg_privacy = 3
     89 };
     90 
     91 /*
     92 struct chrand_ret {
     93 	krb5_ui_4 api_version;
     94 	kadm5_ret_t ret;
     95 	int n_keys;
     96 	krb5_keyblock *keys;
     97 };
     98 */
     99 
    100 
    101 struct gcred {
    102     uint32_t version;
    103     uint32_t proc;
    104     uint32_t seq_num;
    105     uint32_t service;
    106     krb5_data handle;
    107 };
    108 
    109 static int
    110 parse_name(const unsigned char *p, size_t len,
    111 	   const gss_OID oid, char **name)
    112 {
    113     size_t l;
    114 
    115     if (len < 4)
    116 	return 1;
    117 
    118     /* TOK_ID */
    119     if (memcmp(p, "\x04\x01", 2) != 0)
    120 	return 1;
    121     len -= 2;
    122     p += 2;
    123 
    124     /* MECH_LEN */
    125     l = (p[0] << 8) | p[1];
    126     len -= 2;
    127     p += 2;
    128     if (l < 2 || len < l)
    129 	return 1;
    130 
    131     /* oid wrapping */
    132     if (p[0] != 6 || p[1] != l - 2)
    133 	return 1;
    134     p += 2;
    135     l -= 2;
    136     len -= 2;
    137 
    138     /* MECH */
    139     if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
    140 	return 1;
    141     len -= l;
    142     p += l;
    143 
    144     /* MECHNAME_LEN */
    145     if (len < 4)
    146 	return 1;
    147     l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
    148     len -= 4;
    149     p += 4;
    150 
    151     /* MECH NAME */
    152     if (len != l)
    153 	return 1;
    154 
    155     *name = malloc(l + 1);
    156     INSIST(*name != NULL);
    157     memcpy(*name, p, l);
    158     (*name)[l] = '\0';
    159 
    160     return 0;
    161 }
    162 
    163 
    164 
    165 static void
    166 gss_error(krb5_context contextp,
    167 	  gss_OID mech, OM_uint32 type, OM_uint32 error)
    168 {
    169     OM_uint32 new_stat;
    170     OM_uint32 msg_ctx = 0;
    171     gss_buffer_desc status_string;
    172     OM_uint32 ret;
    173 
    174     do {
    175 	ret = gss_display_status (&new_stat,
    176 				  error,
    177 				  type,
    178 				  mech,
    179 				  &msg_ctx,
    180 				  &status_string);
    181 	krb5_warnx(contextp, "%.*s",
    182 		   (int)status_string.length,
    183 		   (char *)status_string.value);
    184 	gss_release_buffer (&new_stat, &status_string);
    185     } while (!GSS_ERROR(ret) && msg_ctx != 0);
    186 }
    187 
    188 static void
    189 gss_print_errors (krb5_context contextp,
    190 		  OM_uint32 maj_stat, OM_uint32 min_stat)
    191 {
    192     gss_error(contextp, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
    193     gss_error(contextp, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
    194 }
    195 
    196 static int
    197 read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
    198 {
    199     char buf[1024];
    200 
    201     while (len) {
    202 	size_t tlen = len;
    203 	ssize_t slen;
    204 
    205 	if (tlen > sizeof(buf))
    206 	    tlen = sizeof(buf);
    207 
    208 	slen = krb5_storage_read(sp, buf, tlen);
    209 	INSIST((size_t)slen == tlen);
    210 
    211 	slen = krb5_storage_write(msg, buf, tlen);
    212 	INSIST((size_t)slen == tlen);
    213 
    214 	len -= tlen;
    215     }
    216     return 0;
    217 }
    218 
    219 static int
    220 collect_framents(krb5_storage *sp, krb5_storage *msg)
    221 {
    222     krb5_error_code ret;
    223     uint32_t len;
    224     int last_fragment;
    225     size_t total_len = 0;
    226 
    227     do {
    228 	ret = krb5_ret_uint32(sp, &len);
    229 	if (ret)
    230 	    return ret;
    231 
    232 	last_fragment = (len & LAST_FRAGMENT);
    233 	len &= ~LAST_FRAGMENT;
    234 
    235 	CHECK(read_data(sp, msg, len));
    236 	total_len += len;
    237 
    238     } while(!last_fragment || total_len == 0);
    239 
    240     return 0;
    241 }
    242 
    243 static krb5_error_code
    244 store_data_xdr(krb5_storage *sp, krb5_data data)
    245 {
    246     krb5_error_code ret;
    247     size_t res;
    248 
    249     ret = krb5_store_data(sp, data);
    250     if (ret)
    251 	return ret;
    252     res = 4 - (data.length % 4);
    253     if (res != 4) {
    254 	static const char zero[4] = { 0, 0, 0, 0 };
    255 
    256 	ret = krb5_storage_write(sp, zero, res);
    257 	if((size_t)ret != res)
    258 	    return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
    259     }
    260     return 0;
    261 }
    262 
    263 static krb5_error_code
    264 ret_data_xdr(krb5_storage *sp, krb5_data *data)
    265 {
    266     krb5_error_code ret;
    267     ret = krb5_ret_data(sp, data);
    268     if (ret)
    269 	return ret;
    270 
    271     if ((data->length % 4) != 0) {
    272 	char buf[4];
    273 	size_t res;
    274 
    275 	res = 4 - (data->length % 4);
    276 	if (res != 4) {
    277 	    ret = krb5_storage_read(sp, buf, res);
    278 	    if((size_t)ret != res)
    279 		return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
    280 	}
    281     }
    282     return 0;
    283 }
    284 
    285 static krb5_error_code
    286 ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
    287 {
    288     krb5_error_code ret;
    289     ret = krb5_ret_uint32(msg, &ao->flavor);
    290     if (ret) return ret;
    291     ret = ret_data_xdr(msg, &ao->data);
    292     return ret;
    293 }
    294 
    295 static int
    296 ret_gcred(krb5_data *data, struct gcred *gcred)
    297 {
    298     krb5_storage *sp;
    299 
    300     memset(gcred, 0, sizeof(*gcred));
    301 
    302     sp = krb5_storage_from_data(data);
    303     INSIST(sp != NULL);
    304 
    305     CHECK(krb5_ret_uint32(sp, &gcred->version));
    306     CHECK(krb5_ret_uint32(sp, &gcred->proc));
    307     CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
    308     CHECK(krb5_ret_uint32(sp, &gcred->service));
    309     CHECK(ret_data_xdr(sp, &gcred->handle));
    310 
    311     krb5_storage_free(sp);
    312 
    313     return 0;
    314 }
    315 
    316 static krb5_error_code
    317 store_gss_init_res(krb5_storage *sp, krb5_data handle,
    318 		   OM_uint32 maj_stat, OM_uint32 min_stat,
    319 		   uint32_t seq_window, gss_buffer_t gout)
    320 {
    321     krb5_error_code ret;
    322     krb5_data out;
    323 
    324     out.data = gout->value;
    325     out.length = gout->length;
    326 
    327     ret = store_data_xdr(sp, handle);
    328     if (ret) return ret;
    329     ret = krb5_store_uint32(sp, maj_stat);
    330     if (ret) return ret;
    331     ret = krb5_store_uint32(sp, min_stat);
    332     if (ret) return ret;
    333     ret = store_data_xdr(sp, out);
    334     return ret;
    335 }
    336 
    337 static int
    338 store_string_xdr(krb5_storage *sp, const char *str)
    339 {
    340     krb5_data c;
    341     if (str) {
    342 	c.data = rk_UNCONST(str);
    343 	c.length = strlen(str) + 1;
    344     } else
    345 	krb5_data_zero(&c);
    346 
    347     return store_data_xdr(sp, c);
    348 }
    349 
    350 static int
    351 ret_string_xdr(krb5_storage *sp, char **str)
    352 {
    353     krb5_data c;
    354     *str = NULL;
    355     CHECK(ret_data_xdr(sp, &c));
    356     if (c.length) {
    357 	*str = malloc(c.length + 1);
    358 	INSIST(*str != NULL);
    359 	memcpy(*str, c.data, c.length);
    360 	(*str)[c.length] = '\0';
    361     }
    362     krb5_data_free(&c);
    363     return 0;
    364 }
    365 
    366 static int
    367 store_principal_xdr(krb5_context contextp,
    368 		    krb5_storage *sp,
    369 		    krb5_principal p)
    370 {
    371     char *str;
    372     CHECK(krb5_unparse_name(contextp, p, &str));
    373     CHECK(store_string_xdr(sp, str));
    374     free(str);
    375     return 0;
    376 }
    377 
    378 static int
    379 ret_principal_xdr(krb5_context contextp,
    380 		  krb5_storage *sp,
    381 		  krb5_principal *p)
    382 {
    383     char *str;
    384     *p = NULL;
    385     CHECK(ret_string_xdr(sp, &str));
    386     if (str) {
    387 	CHECK(krb5_parse_name(contextp, str, p));
    388 	free(str);
    389     }
    390     return 0;
    391 }
    392 
    393 static int
    394 store_principal_ent(krb5_context contextp,
    395 		    krb5_storage *sp,
    396 		    kadm5_principal_ent_rec *ent)
    397 {
    398     int i;
    399 
    400     CHECK(store_principal_xdr(contextp, sp, ent->principal));
    401     CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
    402     CHECK(krb5_store_uint32(sp, ent->pw_expiration));
    403     CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
    404     CHECK(krb5_store_uint32(sp, ent->max_life));
    405     CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
    406     if (ent->mod_name)
    407 	CHECK(store_principal_xdr(contextp, sp, ent->mod_name));
    408     CHECK(krb5_store_uint32(sp, ent->mod_date));
    409     CHECK(krb5_store_uint32(sp, ent->attributes));
    410     CHECK(krb5_store_uint32(sp, ent->kvno));
    411     CHECK(krb5_store_uint32(sp, ent->mkvno));
    412     CHECK(store_string_xdr(sp, ent->policy));
    413     CHECK(krb5_store_int32(sp, ent->aux_attributes));
    414     CHECK(krb5_store_int32(sp, ent->max_renewable_life));
    415     CHECK(krb5_store_int32(sp, ent->last_success));
    416     CHECK(krb5_store_int32(sp, ent->last_failed));
    417     CHECK(krb5_store_int32(sp, ent->fail_auth_count));
    418     CHECK(krb5_store_int32(sp, ent->n_key_data));
    419     CHECK(krb5_store_int32(sp, ent->n_tl_data));
    420     CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
    421     if (ent->n_tl_data) {
    422 	krb5_tl_data *tp;
    423 
    424 	for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
    425 	    krb5_data c;
    426 	    c.length = tp->tl_data_length;
    427 	    c.data = tp->tl_data_contents;
    428 
    429 	    CHECK(krb5_store_int32(sp, 0)); /* last item */
    430 	    CHECK(krb5_store_int32(sp, tp->tl_data_type));
    431 	    CHECK(store_data_xdr(sp, c));
    432 	}
    433 	CHECK(krb5_store_int32(sp, 1)); /* last item */
    434     }
    435 
    436     CHECK(krb5_store_int32(sp, ent->n_key_data));
    437     for (i = 0; i < ent->n_key_data; i++) {
    438 	CHECK(krb5_store_uint32(sp, 2));
    439 	CHECK(krb5_store_uint32(sp, ent->kvno));
    440 	CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
    441 	CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
    442     }
    443 
    444     return 0;
    445 }
    446 
    447 static int
    448 ret_principal_ent(krb5_context contextp,
    449 		  krb5_storage *sp,
    450 		  kadm5_principal_ent_rec *ent)
    451 {
    452     uint32_t flag, num;
    453     size_t i;
    454 
    455     memset(ent, 0, sizeof(*ent));
    456 
    457     CHECK(ret_principal_xdr(contextp, sp, &ent->principal));
    458     CHECK(krb5_ret_uint32(sp, &flag));
    459     ent->princ_expire_time = flag;
    460     CHECK(krb5_ret_uint32(sp, &flag));
    461     ent->pw_expiration = flag;
    462     CHECK(krb5_ret_uint32(sp, &flag));
    463     ent->last_pwd_change = flag;
    464     CHECK(krb5_ret_uint32(sp, &flag));
    465     ent->max_life = flag;
    466     CHECK(krb5_ret_uint32(sp, &flag));
    467     if (flag == 0)
    468 	CHECK(ret_principal_xdr(contextp, sp, &ent->mod_name));
    469     CHECK(krb5_ret_uint32(sp, &flag));
    470     ent->mod_date = flag;
    471     CHECK(krb5_ret_uint32(sp, &flag));
    472     ent->attributes = flag;
    473     CHECK(krb5_ret_uint32(sp, &flag));
    474     ent->kvno = flag;
    475     CHECK(krb5_ret_uint32(sp, &flag));
    476     ent->mkvno = flag;
    477     CHECK(ret_string_xdr(sp, &ent->policy));
    478     CHECK(krb5_ret_uint32(sp, &flag));
    479     ent->aux_attributes = flag;
    480     CHECK(krb5_ret_uint32(sp, &flag));
    481     ent->max_renewable_life = flag;
    482     CHECK(krb5_ret_uint32(sp, &flag));
    483     ent->last_success = flag;
    484     CHECK(krb5_ret_uint32(sp, &flag));
    485     ent->last_failed = flag;
    486     CHECK(krb5_ret_uint32(sp, &flag));
    487     ent->fail_auth_count = flag;
    488     CHECK(krb5_ret_uint32(sp, &flag));
    489     ent->n_key_data = flag;
    490     CHECK(krb5_ret_uint32(sp, &flag));
    491     ent->n_tl_data = flag;
    492     CHECK(krb5_ret_uint32(sp, &flag));
    493     if (flag == 0) {
    494 	krb5_tl_data **tp = &ent->tl_data;
    495 	size_t count = 0;
    496 
    497 	while(1) {
    498 	    krb5_data c;
    499 	    CHECK(krb5_ret_uint32(sp, &flag)); /* last item */
    500 	    if (flag)
    501 		break;
    502 	    *tp = calloc(1, sizeof(**tp));
    503 	    INSIST(*tp != NULL);
    504 	    CHECK(krb5_ret_uint32(sp, &flag));
    505 	    (*tp)->tl_data_type = flag;
    506 	    CHECK(ret_data_xdr(sp, &c));
    507 	    (*tp)->tl_data_length = c.length;
    508 	    (*tp)->tl_data_contents = c.data;
    509 	    tp = &(*tp)->tl_data_next;
    510 
    511 	    count++;
    512 	}
    513 	INSIST((size_t)ent->n_tl_data == count);
    514     } else {
    515 	INSIST(ent->n_tl_data == 0);
    516     }
    517 
    518     CHECK(krb5_ret_uint32(sp, &num));
    519     INSIST(num == (uint32_t)ent->n_key_data);
    520 
    521     ent->key_data = calloc(num, sizeof(ent->key_data[0]));
    522     INSIST(ent->key_data != NULL);
    523 
    524     for (i = 0; i < num; i++) {
    525 	CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
    526 	INSIST(flag > 1);
    527 	CHECK(krb5_ret_uint32(sp, &flag));
    528 	ent->kvno = flag;
    529 	CHECK(krb5_ret_uint32(sp, &flag));
    530 	ent->key_data[i].key_data_type[0] = flag;
    531 	CHECK(krb5_ret_uint32(sp, &flag));
    532 	ent->key_data[i].key_data_type[1] = flag;
    533     }
    534 
    535     return 0;
    536 }
    537 
    538 /*
    539  *
    540  */
    541 
    542 static void
    543 proc_create_principal(kadm5_server_context *contextp,
    544 		      krb5_storage *in,
    545 		      krb5_storage *out)
    546 {
    547     uint32_t version, mask;
    548     kadm5_principal_ent_rec ent;
    549     krb5_error_code ret;
    550     char *password;
    551 
    552     memset(&ent, 0, sizeof(ent));
    553 
    554     CHECK(krb5_ret_uint32(in, &version));
    555     INSIST(version == VERSION2);
    556     CHECK(ret_principal_ent(contextp->context, in, &ent));
    557     CHECK(krb5_ret_uint32(in, &mask));
    558     CHECK(ret_string_xdr(in, &password));
    559 
    560     INSIST(ent.principal);
    561 
    562 
    563     ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal);
    564     if (ret)
    565 	goto fail;
    566 
    567     ret = kadm5_create_principal(contextp, &ent, mask, password);
    568 
    569  fail:
    570     krb5_warn(contextp->context, ret, "create principal");
    571     CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
    572     CHECK(krb5_store_uint32(out, ret)); /* code */
    573 
    574     free(password);
    575     kadm5_free_principal_ent(contextp, &ent);
    576 }
    577 
    578 static void
    579 proc_delete_principal(kadm5_server_context *contextp,
    580 		      krb5_storage *in,
    581 		      krb5_storage *out)
    582 {
    583     uint32_t version;
    584     krb5_principal princ;
    585     krb5_error_code ret;
    586 
    587     CHECK(krb5_ret_uint32(in, &version));
    588     INSIST(version == VERSION2);
    589     CHECK(ret_principal_xdr(contextp->context, in, &princ));
    590 
    591     ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
    592     if (ret)
    593 	goto fail;
    594 
    595     ret = kadm5_delete_principal(contextp, princ);
    596 
    597  fail:
    598     krb5_warn(contextp->context, ret, "delete principal");
    599     CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
    600     CHECK(krb5_store_uint32(out, ret)); /* code */
    601 
    602     krb5_free_principal(contextp->context, princ);
    603 }
    604 
    605 static void
    606 proc_get_principal(kadm5_server_context *contextp,
    607 		   krb5_storage *in,
    608 		   krb5_storage *out)
    609 {
    610     uint32_t version, mask;
    611     krb5_principal princ;
    612     kadm5_principal_ent_rec ent;
    613     krb5_error_code ret;
    614 
    615     memset(&ent, 0, sizeof(ent));
    616 
    617     CHECK(krb5_ret_uint32(in, &version));
    618     INSIST(version == VERSION2);
    619     CHECK(ret_principal_xdr(contextp->context, in, &princ));
    620     CHECK(krb5_ret_uint32(in, &mask));
    621 
    622     ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
    623     if(ret)
    624 	goto fail;
    625 
    626     ret = kadm5_get_principal(contextp, princ, &ent, mask);
    627 
    628  fail:
    629     krb5_warn(contextp->context, ret, "get principal principal");
    630 
    631     CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
    632     CHECK(krb5_store_uint32(out, ret)); /* code */
    633     if (ret == 0) {
    634 	CHECK(store_principal_ent(contextp->context, out, &ent));
    635     }
    636     krb5_free_principal(contextp->context, princ);
    637     kadm5_free_principal_ent(contextp, &ent);
    638 }
    639 
    640 static void
    641 proc_chrand_principal_v2(kadm5_server_context *contextp,
    642 			 krb5_storage *in,
    643 			 krb5_storage *out)
    644 {
    645     krb5_error_code ret;
    646     krb5_principal princ;
    647     uint32_t version;
    648     krb5_keyblock *new_keys;
    649     int n_keys;
    650 
    651     CHECK(krb5_ret_uint32(in, &version));
    652     INSIST(version == VERSION2);
    653     CHECK(ret_principal_xdr(contextp->context, in, &princ));
    654 
    655     ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
    656     if(ret)
    657 	goto fail;
    658 
    659     ret = kadm5_randkey_principal(contextp, princ,
    660 				  &new_keys, &n_keys);
    661 
    662  fail:
    663     krb5_warn(contextp->context, ret, "rand key principal");
    664 
    665     CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
    666     CHECK(krb5_store_uint32(out, ret));
    667     if (ret == 0) {
    668 	int i;
    669 	CHECK(krb5_store_int32(out, n_keys));
    670 
    671 	for(i = 0; i < n_keys; i++){
    672 	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
    673 	    CHECK(store_data_xdr(out, new_keys[i].keyvalue));
    674 	    krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
    675 	}
    676 	free(new_keys);
    677     }
    678     krb5_free_principal(contextp->context, princ);
    679 }
    680 
    681 static void
    682 proc_init(kadm5_server_context *contextp,
    683 	  krb5_storage *in,
    684 	  krb5_storage *out)
    685 {
    686     CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
    687     CHECK(krb5_store_uint32(out, 0)); /* code */
    688     CHECK(krb5_store_uint32(out, 0)); /* code */
    689 }
    690 
    691 struct krb5_proc {
    692     const char *name;
    693     void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
    694 } procs[] = {
    695     { "NULL", NULL },
    696     { "create principal", proc_create_principal },
    697     { "delete principal", proc_delete_principal },
    698     { "modify principal", NULL },
    699     { "rename principal", NULL },
    700     { "get principal", proc_get_principal },
    701     { "chpass principal", NULL },
    702     { "chrand principal", proc_chrand_principal_v2 },
    703     { "create policy", NULL },
    704     { "delete policy", NULL },
    705     { "modify policy", NULL },
    706     { "get policy", NULL },
    707     { "get privs", NULL },
    708     { "init", proc_init },
    709     { "get principals", NULL },
    710     { "get polices", NULL },
    711     { "setkey principal", NULL },
    712     { "setkey principal v4", NULL },
    713     { "create principal v3", NULL },
    714     { "chpass principal v3", NULL },
    715     { "chrand principal v3", NULL },
    716     { "setkey principal v3", NULL }
    717 };
    718 
    719 static krb5_error_code
    720 copyheader(krb5_storage *sp, krb5_data *data)
    721 {
    722     off_t off;
    723     ssize_t sret;
    724 
    725     off = krb5_storage_seek(sp, 0, SEEK_CUR);
    726 
    727     CHECK(krb5_data_alloc(data, off));
    728     INSIST((size_t)off == data->length);
    729     krb5_storage_seek(sp, 0, SEEK_SET);
    730     sret = krb5_storage_read(sp, data->data, data->length);
    731     INSIST(sret == off);
    732     INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
    733 
    734     return 0;
    735 }
    736 
    737 struct gctx {
    738     krb5_data handle;
    739     gss_ctx_id_t ctx;
    740     uint32_t seq_num;
    741     int done;
    742     int inprogress;
    743 };
    744 
    745 static int
    746 process_stream(krb5_context contextp,
    747 	       unsigned char *buf, size_t ilen,
    748 	       krb5_storage *sp)
    749 {
    750     krb5_error_code ret;
    751     krb5_storage *msg, *reply, *dreply;
    752     OM_uint32 maj_stat, min_stat;
    753     gss_buffer_desc gin, gout;
    754     struct gctx gctx;
    755     void *server_handle = NULL;
    756 
    757     memset(&gctx, 0, sizeof(gctx));
    758 
    759     msg = krb5_storage_emem();
    760     reply = krb5_storage_emem();
    761     dreply = krb5_storage_emem();
    762 
    763     /*
    764      * First packet comes partly from the caller
    765      */
    766 
    767     INSIST(ilen >= 4);
    768 
    769     while (1) {
    770 	struct call_header chdr;
    771 	struct gcred gcred;
    772 	uint32_t mtype;
    773 	krb5_data headercopy;
    774 
    775 	krb5_storage_truncate(dreply, 0);
    776 	krb5_storage_truncate(reply, 0);
    777 	krb5_storage_truncate(msg, 0);
    778 
    779 	krb5_data_zero(&headercopy);
    780 	memset(&chdr, 0, sizeof(chdr));
    781 	memset(&gcred, 0, sizeof(gcred));
    782 
    783 	/*
    784 	 * This is very icky to handle the the auto-detection between
    785 	 * the Heimdal protocol and the MIT ONC-RPC based protocol.
    786 	 */
    787 
    788 	if (ilen) {
    789 	    int last_fragment;
    790 	    unsigned long len;
    791 	    ssize_t slen;
    792 	    unsigned char tmp[4];
    793 
    794 	    if (ilen < 4) {
    795 		memcpy(tmp, buf, ilen);
    796 		slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
    797 		INSIST((size_t)slen == sizeof(tmp) - ilen);
    798 
    799 		ilen = sizeof(tmp);
    800 		buf = tmp;
    801 	    }
    802 	    INSIST(ilen >= 4);
    803 
    804 	    _krb5_get_int(buf, &len, 4);
    805 	    last_fragment = (len & LAST_FRAGMENT) != 0;
    806 	    len &= ~LAST_FRAGMENT;
    807 
    808 	    ilen -= 4;
    809 	    buf += 4;
    810 
    811 	    if (ilen) {
    812 		if (len < ilen) {
    813 		    slen = krb5_storage_write(msg, buf, len);
    814 		    INSIST((size_t)slen == len);
    815 		    ilen -= len;
    816 		    len = 0;
    817 		} else {
    818 		    slen = krb5_storage_write(msg, buf, ilen);
    819 		    INSIST((size_t)slen == ilen);
    820 		    len -= ilen;
    821 		}
    822 	    }
    823 
    824 	    CHECK(read_data(sp, msg, len));
    825 
    826 	    if (!last_fragment) {
    827 		ret = collect_framents(sp, msg);
    828 		if (ret == HEIM_ERR_EOF)
    829 		    krb5_errx(contextp, 0, "client disconnected");
    830 		INSIST(ret == 0);
    831 	    }
    832 	} else {
    833 
    834 	    ret = collect_framents(sp, msg);
    835 	    if (ret == HEIM_ERR_EOF)
    836 		krb5_errx(contextp, 0, "client disconnected");
    837 	    INSIST(ret == 0);
    838 	}
    839 	krb5_storage_seek(msg, 0, SEEK_SET);
    840 
    841 	CHECK(krb5_ret_uint32(msg, &chdr.xid));
    842 	CHECK(krb5_ret_uint32(msg, &mtype));
    843 	CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
    844 	CHECK(krb5_ret_uint32(msg, &chdr.prog));
    845 	CHECK(krb5_ret_uint32(msg, &chdr.vers));
    846 	CHECK(krb5_ret_uint32(msg, &chdr.proc));
    847 	CHECK(ret_auth_opaque(msg, &chdr.cred));
    848 	CHECK(copyheader(msg, &headercopy));
    849 	CHECK(ret_auth_opaque(msg, &chdr.verf));
    850 
    851 	INSIST(chdr.rpcvers == RPC_VERSION);
    852 	INSIST(chdr.prog == KADM_SERVER);
    853 	INSIST(chdr.vers == VVERSION);
    854 	INSIST(chdr.cred.flavor == FLAVOR_GSS);
    855 
    856 	CHECK(ret_gcred(&chdr.cred.data, &gcred));
    857 
    858 	INSIST(gcred.version == FLAVOR_GSS_VERSION);
    859 
    860 	if (gctx.done) {
    861 	    INSIST(chdr.verf.flavor == FLAVOR_GSS);
    862 
    863 	    /* from first byte to last of credential */
    864 	    gin.value = headercopy.data;
    865 	    gin.length = headercopy.length;
    866 	    gout.value = chdr.verf.data.data;
    867 	    gout.length = chdr.verf.data.length;
    868 
    869 	    maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
    870 	    INSIST(maj_stat == GSS_S_COMPLETE);
    871 	}
    872 
    873 	switch(gcred.proc) {
    874 	case RPG_DATA: {
    875 	    krb5_data data;
    876 	    int conf_state;
    877 	    uint32_t seq;
    878 	    krb5_storage *sp1;
    879 
    880 	    INSIST(gcred.service == rpg_privacy);
    881 
    882 	    INSIST(gctx.done);
    883 
    884 	    INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
    885 
    886 	    CHECK(ret_data_xdr(msg, &data));
    887 
    888 	    gin.value = data.data;
    889 	    gin.length = data.length;
    890 
    891 	    maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
    892 				  &conf_state, NULL);
    893 	    krb5_data_free(&data);
    894 	    INSIST(maj_stat == GSS_S_COMPLETE);
    895 	    INSIST(conf_state != 0);
    896 
    897 	    sp1 = krb5_storage_from_mem(gout.value, gout.length);
    898 	    INSIST(sp1 != NULL);
    899 
    900 	    CHECK(krb5_ret_uint32(sp1, &seq));
    901 	    INSIST (seq == gcred.seq_num);
    902 
    903 	    /*
    904 	     * Check sequence number
    905 	     */
    906 	    INSIST(seq > gctx.seq_num);
    907 	    gctx.seq_num = seq;
    908 
    909 	    /*
    910 	     * If contextp is setup, priv data have the seq_num stored
    911 	     * first in the block, so add it here before users data is
    912 	     * added.
    913 	     */
    914 	    CHECK(krb5_store_uint32(dreply, gctx.seq_num));
    915 
    916 	    if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) {
    917 		krb5_warnx(contextp, "proc number out of array");
    918 	    } else if (procs[chdr.proc].func == NULL) {
    919 		krb5_warnx(contextp, "proc '%s' never implemented",
    920 			  procs[chdr.proc].name);
    921 	    } else {
    922 		krb5_warnx(contextp, "proc %s", procs[chdr.proc].name);
    923 		INSIST(server_handle != NULL);
    924 		(*procs[chdr.proc].func)(server_handle, sp, dreply);
    925 	    }
    926 	    krb5_storage_free(sp);
    927 	    gss_release_buffer(&min_stat, &gout);
    928 
    929 	    break;
    930 	}
    931 	case RPG_INIT:
    932 	    INSIST(gctx.inprogress == 0);
    933 	    INSIST(gctx.ctx == NULL);
    934 
    935 	    gctx.inprogress = 1;
    936 	    /* FALLTHROUGH */
    937 	case RPG_CONTINUE_INIT: {
    938 	    gss_name_t src_name = GSS_C_NO_NAME;
    939 	    krb5_data in;
    940 
    941 	    INSIST(gctx.inprogress);
    942 
    943 	    CHECK(ret_data_xdr(msg, &in));
    944 
    945 	    gin.value = in.data;
    946 	    gin.length = in.length;
    947 	    gout.value = NULL;
    948 	    gout.length = 0;
    949 
    950 	    maj_stat = gss_accept_sec_context(&min_stat,
    951 					      &gctx.ctx,
    952 					      GSS_C_NO_CREDENTIAL,
    953 					      &gin,
    954 					      GSS_C_NO_CHANNEL_BINDINGS,
    955 					      &src_name,
    956 					      NULL,
    957 					      &gout,
    958 					      NULL,
    959 					      NULL,
    960 					      NULL);
    961 	    if (GSS_ERROR(maj_stat)) {
    962 		gss_print_errors(contextp, maj_stat, min_stat);
    963 		krb5_errx(contextp, 1, "gss error, exit");
    964 	    }
    965 	    if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
    966 		kadm5_config_params realm_params;
    967 		gss_buffer_desc bufp;
    968 		char *client;
    969 
    970 		gctx.done = 1;
    971 
    972 		memset(&realm_params, 0, sizeof(realm_params));
    973 
    974 		maj_stat = gss_export_name(&min_stat, src_name, &bufp);
    975 		INSIST(maj_stat == GSS_S_COMPLETE);
    976 
    977 		CHECK(parse_name(bufp.value, bufp.length,
    978 				 GSS_KRB5_MECHANISM, &client));
    979 
    980 		gss_release_buffer(&min_stat, &bufp);
    981 
    982 		krb5_warnx(contextp, "%s connected", client);
    983 
    984 		ret = kadm5_s_init_with_password_ctx(contextp,
    985 						     client,
    986 						     NULL,
    987 						     KADM5_ADMIN_SERVICE,
    988 						     &realm_params,
    989 						     0, 0,
    990 						     &server_handle);
    991 		INSIST(ret == 0);
    992 	    }
    993 
    994 	    INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
    995 
    996 	    CHECK(krb5_store_uint32(dreply, 0));
    997 	    CHECK(store_gss_init_res(dreply, gctx.handle,
    998 				     maj_stat, min_stat, 1, &gout));
    999 	    if (gout.value)
   1000 		gss_release_buffer(&min_stat, &gout);
   1001 	    if (src_name)
   1002 		gss_release_name(&min_stat, &src_name);
   1003 
   1004 	    break;
   1005 	}
   1006 	case RPG_DESTROY:
   1007 	    krb5_errx(contextp, 1, "client destroyed gss contextp");
   1008 	default:
   1009 	    krb5_errx(contextp, 1, "client sent unknown gsscode %d",
   1010 		      (int)gcred.proc);
   1011 	}
   1012 
   1013 	krb5_data_free(&gcred.handle);
   1014 	krb5_data_free(&chdr.cred.data);
   1015 	krb5_data_free(&chdr.verf.data);
   1016 	krb5_data_free(&headercopy);
   1017 
   1018 	CHECK(krb5_store_uint32(reply, chdr.xid));
   1019 	CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
   1020 	CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
   1021 
   1022 	if (!gctx.done) {
   1023 	    krb5_data data;
   1024 
   1025 	    CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
   1026 	    CHECK(krb5_store_uint32(reply, 0)); /* length */
   1027 
   1028 	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
   1029 
   1030 	    CHECK(krb5_storage_to_data(dreply, &data));
   1031 	    INSIST((size_t)krb5_storage_write(reply, data.data, data.length) == data.length);
   1032 	    krb5_data_free(&data);
   1033 
   1034 	} else {
   1035 	    uint32_t seqnum = htonl(gctx.seq_num);
   1036 	    krb5_data data;
   1037 
   1038 	    gin.value = &seqnum;
   1039 	    gin.length = sizeof(seqnum);
   1040 
   1041 	    maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
   1042 	    INSIST(maj_stat == GSS_S_COMPLETE);
   1043 
   1044 	    data.data = gout.value;
   1045 	    data.length = gout.length;
   1046 
   1047 	    CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
   1048 	    CHECK(store_data_xdr(reply, data));
   1049 	    gss_release_buffer(&min_stat, &gout);
   1050 
   1051 	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
   1052 
   1053 	    CHECK(krb5_storage_to_data(dreply, &data));
   1054 
   1055 	    if (gctx.inprogress) {
   1056 		ssize_t sret;
   1057 		gctx.inprogress = 0;
   1058 		sret = krb5_storage_write(reply, data.data, data.length);
   1059 		INSIST((size_t)sret == data.length);
   1060 		krb5_data_free(&data);
   1061 	    } else {
   1062 		int conf_state;
   1063 
   1064 		gin.value = data.data;
   1065 		gin.length = data.length;
   1066 
   1067 		maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
   1068 				    &gin, &conf_state, &gout);
   1069 		INSIST(maj_stat == GSS_S_COMPLETE);
   1070 		INSIST(conf_state != 0);
   1071 		krb5_data_free(&data);
   1072 
   1073 		data.data = gout.value;
   1074 		data.length = gout.length;
   1075 
   1076 		store_data_xdr(reply, data);
   1077 		gss_release_buffer(&min_stat, &gout);
   1078 	    }
   1079 	}
   1080 
   1081 	{
   1082 	    krb5_data data;
   1083 	    ssize_t sret;
   1084 	    CHECK(krb5_storage_to_data(reply, &data));
   1085 	    CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
   1086 	    sret = krb5_storage_write(sp, data.data, data.length);
   1087 	    INSIST((size_t)sret == data.length);
   1088 	    krb5_data_free(&data);
   1089 	}
   1090 
   1091     }
   1092 }
   1093 
   1094 
   1095 int
   1096 handle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock)
   1097 {
   1098     krb5_storage *sp;
   1099 
   1100     dcontext = contextp;
   1101 
   1102     sp = krb5_storage_from_socket(sock);
   1103     INSIST(sp != NULL);
   1104 
   1105     process_stream(contextp, buf, len, sp);
   1106 
   1107     return 0;
   1108 }
   1109