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