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