1 /*	$NetBSD: rpc.c,v 1.4 2023/06/19 21:41:41 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Kungliga Tekniska Högskolan
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
parse_name(const unsigned char * p,size_t len,const gss_OID oid,char ** name)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
gss_error(krb5_context contextp,gss_OID mech,OM_uint32 type,OM_uint32 error)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
gss_print_errors(krb5_context contextp,OM_uint32 maj_stat,OM_uint32 min_stat)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
read_data(krb5_storage * sp,krb5_storage * msg,size_t len)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
collect_framents(krb5_storage * sp,krb5_storage * msg)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
store_data_xdr(krb5_storage * sp,krb5_data data)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
ret_data_xdr(krb5_storage * sp,krb5_data * data)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
ret_auth_opaque(krb5_storage * msg,struct opaque_auth * ao)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
ret_gcred(krb5_data * data,struct gcred * gcred)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
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)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
store_string_xdr(krb5_storage * sp,const char * str)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
ret_string_xdr(krb5_storage * sp,char ** str)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
store_principal_xdr(krb5_context contextp,krb5_storage * sp,krb5_principal p)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
ret_principal_xdr(krb5_context contextp,krb5_storage * sp,krb5_principal * p)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
store_principal_ent(krb5_context contextp,krb5_storage * sp,kadm5_principal_ent_rec * ent)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
ret_principal_ent(krb5_context contextp,krb5_storage * sp,kadm5_principal_ent_rec * ent)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
proc_create_principal(kadm5_server_context * contextp,krb5_storage * in,krb5_storage * out)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
proc_delete_principal(kadm5_server_context * contextp,krb5_storage * in,krb5_storage * out)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
proc_get_principal(kadm5_server_context * contextp,krb5_storage * in,krb5_storage * out)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
proc_chrand_principal_v2(kadm5_server_context * contextp,krb5_storage * in,krb5_storage * out)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
proc_init(kadm5_server_context * contextp,krb5_storage * in,krb5_storage * out)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
copyheader(krb5_storage * sp,krb5_data * data)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
process_stream(krb5_context contextp,unsigned char * buf,size_t ilen,krb5_storage * sp)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
handle_mit(krb5_context contextp,void * buf,size_t len,krb5_socket_t sock)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