lib/gssapi/krb5: implement GSS_C_CHANNEL_BOUND_FLAG for gss_init_sec_context()
[heimdal.git] / kadmin / rpc.c
blob8a176da6399bfbb30d911fdc521efaae5055c958
1 /*
2 * Copyright (c) 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "kadmin_locl.h"
36 #include <gssapi.h>
37 #include <gssapi_krb5.h>
38 #include <gssapi_spnego.h>
40 #define CHECK(x) \
41 do { \
42 int __r; \
43 if ((__r = (x))) { \
44 krb5_errx(dcontext, 1, "Failed (%d) on %s:%d", \
45 __r, __FILE__, __LINE__); \
46 } \
47 } while(0)
49 static krb5_context dcontext;
51 #define INSIST(x) CHECK(!(x))
53 #define VERSION2 0x12345702
55 #define LAST_FRAGMENT 0x80000000
57 #define RPC_VERSION 2
58 #define KADM_SERVER 2112
59 #define VVERSION 2
60 #define FLAVOR_GSS 6
61 #define FLAVOR_GSS_VERSION 1
63 struct opaque_auth {
64 uint32_t flavor;
65 krb5_data data;
68 struct call_header {
69 uint32_t xid;
70 uint32_t rpcvers;
71 uint32_t prog;
72 uint32_t vers;
73 uint32_t proc;
74 struct opaque_auth cred;
75 struct opaque_auth verf;
78 enum {
79 RPG_DATA = 0,
80 RPG_INIT = 1,
81 RPG_CONTINUE_INIT = 2,
82 RPG_DESTROY = 3
85 enum {
86 rpg_privacy = 3
90 struct chrand_ret {
91 krb5_ui_4 api_version;
92 kadm5_ret_t ret;
93 int n_keys;
94 krb5_keyblock *keys;
99 struct gcred {
100 uint32_t version;
101 uint32_t proc;
102 uint32_t seq_num;
103 uint32_t service;
104 krb5_data handle;
107 static int
108 parse_name(const unsigned char *p, size_t len,
109 const gss_OID oid, char **name)
111 size_t l;
113 if (len < 4)
114 return 1;
116 /* TOK_ID */
117 if (memcmp(p, "\x04\x01", 2) != 0)
118 return 1;
119 len -= 2;
120 p += 2;
122 /* MECH_LEN */
123 l = (p[0] << 8) | p[1];
124 len -= 2;
125 p += 2;
126 if (l < 2 || len < l)
127 return 1;
129 /* oid wrapping */
130 if (p[0] != 6 || p[1] != l - 2)
131 return 1;
132 p += 2;
133 l -= 2;
134 len -= 2;
136 /* MECH */
137 if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
138 return 1;
139 len -= l;
140 p += l;
142 /* MECHNAME_LEN */
143 if (len < 4)
144 return 1;
145 l = (unsigned long)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
146 len -= 4;
147 p += 4;
149 /* MECH NAME */
150 if (len != l)
151 return 1;
153 *name = malloc(l + 1);
154 INSIST(*name != NULL);
155 memcpy(*name, p, l);
156 (*name)[l] = '\0';
158 return 0;
163 static void
164 gss_error(krb5_context contextp,
165 gss_OID mech, OM_uint32 type, OM_uint32 error)
167 OM_uint32 new_stat;
168 OM_uint32 msg_ctx = 0;
169 gss_buffer_desc status_string;
170 OM_uint32 ret;
172 do {
173 ret = gss_display_status (&new_stat,
174 error,
175 type,
176 mech,
177 &msg_ctx,
178 &status_string);
179 krb5_warnx(contextp, "%.*s",
180 (int)status_string.length,
181 (char *)status_string.value);
182 gss_release_buffer (&new_stat, &status_string);
183 } while (!GSS_ERROR(ret) && msg_ctx != 0);
186 static void
187 gss_print_errors (krb5_context contextp,
188 OM_uint32 maj_stat, OM_uint32 min_stat)
190 gss_error(contextp, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
191 gss_error(contextp, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
194 static int
195 read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
197 char buf[1024];
199 while (len) {
200 size_t tlen = len;
201 ssize_t slen;
203 if (tlen > sizeof(buf))
204 tlen = sizeof(buf);
206 slen = krb5_storage_read(sp, buf, tlen);
207 INSIST((size_t)slen == tlen);
209 slen = krb5_storage_write(msg, buf, tlen);
210 INSIST((size_t)slen == tlen);
212 len -= tlen;
214 return 0;
217 static int
218 collect_framents(krb5_storage *sp, krb5_storage *msg)
220 krb5_error_code ret;
221 uint32_t len;
222 int last_fragment;
223 size_t total_len = 0;
225 do {
226 ret = krb5_ret_uint32(sp, &len);
227 if (ret)
228 return ret;
230 last_fragment = (len & LAST_FRAGMENT);
231 len &= ~LAST_FRAGMENT;
233 CHECK(read_data(sp, msg, len));
234 total_len += len;
236 } while(!last_fragment || total_len == 0);
238 return 0;
241 static krb5_error_code
242 store_data_xdr(krb5_storage *sp, krb5_data data)
244 krb5_error_code ret;
245 size_t res;
247 ret = krb5_store_data(sp, data);
248 if (ret)
249 return ret;
250 res = 4 - (data.length % 4);
251 if (res != 4) {
252 static const char zero[4] = { 0, 0, 0, 0 };
254 ret = krb5_storage_write(sp, zero, res);
255 if((size_t)ret != res)
256 return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
258 return 0;
261 static krb5_error_code
262 ret_data_xdr(krb5_storage *sp, krb5_data *data)
264 krb5_error_code ret;
265 ret = krb5_ret_data(sp, data);
266 if (ret)
267 return ret;
269 if ((data->length % 4) != 0) {
270 char buf[4];
271 size_t res;
273 res = 4 - (data->length % 4);
274 if (res != 4) {
275 ret = krb5_storage_read(sp, buf, res);
276 if((size_t)ret != res)
277 return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
280 return 0;
283 static krb5_error_code
284 ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
286 krb5_error_code ret;
287 ret = krb5_ret_uint32(msg, &ao->flavor);
288 if (ret) return ret;
289 ret = ret_data_xdr(msg, &ao->data);
290 return ret;
293 static int
294 ret_gcred(krb5_data *data, struct gcred *gcred)
296 krb5_storage *sp;
298 memset(gcred, 0, sizeof(*gcred));
300 sp = krb5_storage_from_data(data);
301 INSIST(sp != NULL);
303 CHECK(krb5_ret_uint32(sp, &gcred->version));
304 CHECK(krb5_ret_uint32(sp, &gcred->proc));
305 CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
306 CHECK(krb5_ret_uint32(sp, &gcred->service));
307 CHECK(ret_data_xdr(sp, &gcred->handle));
309 krb5_storage_free(sp);
311 return 0;
314 static krb5_error_code
315 store_gss_init_res(krb5_storage *sp, krb5_data handle,
316 OM_uint32 maj_stat, OM_uint32 min_stat,
317 uint32_t seq_window, gss_buffer_t gout)
319 krb5_error_code ret;
320 krb5_data out;
322 out.data = gout->value;
323 out.length = gout->length;
325 ret = store_data_xdr(sp, handle);
326 if (ret) return ret;
327 ret = krb5_store_uint32(sp, maj_stat);
328 if (ret) return ret;
329 ret = krb5_store_uint32(sp, min_stat);
330 if (ret) return ret;
331 ret = store_data_xdr(sp, out);
332 return ret;
335 static int
336 store_string_xdr(krb5_storage *sp, const char *str)
338 krb5_data c;
339 if (str) {
340 c.data = rk_UNCONST(str);
341 c.length = strlen(str) + 1;
342 } else
343 krb5_data_zero(&c);
345 return store_data_xdr(sp, c);
348 static int
349 ret_string_xdr(krb5_storage *sp, char **str)
351 krb5_data c;
352 *str = NULL;
353 CHECK(ret_data_xdr(sp, &c));
354 if (c.length) {
355 *str = malloc(c.length + 1);
356 INSIST(*str != NULL);
357 memcpy(*str, c.data, c.length);
358 (*str)[c.length] = '\0';
360 krb5_data_free(&c);
361 return 0;
364 static int
365 store_principal_xdr(krb5_context contextp,
366 krb5_storage *sp,
367 krb5_principal p)
369 char *str;
370 CHECK(krb5_unparse_name(contextp, p, &str));
371 CHECK(store_string_xdr(sp, str));
372 free(str);
373 return 0;
376 static int
377 ret_principal_xdr(krb5_context contextp,
378 krb5_storage *sp,
379 krb5_principal *p)
381 char *str;
382 *p = NULL;
383 CHECK(ret_string_xdr(sp, &str));
384 if (str) {
385 CHECK(krb5_parse_name(contextp, str, p));
386 free(str);
388 return 0;
391 static int
392 store_principal_ent(krb5_context contextp,
393 krb5_storage *sp,
394 kadm5_principal_ent_rec *ent)
396 int i;
398 CHECK(store_principal_xdr(contextp, sp, ent->principal));
399 CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
400 CHECK(krb5_store_uint32(sp, ent->pw_expiration));
401 CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
402 CHECK(krb5_store_uint32(sp, ent->max_life));
403 CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
404 if (ent->mod_name)
405 CHECK(store_principal_xdr(contextp, sp, ent->mod_name));
406 CHECK(krb5_store_uint32(sp, ent->mod_date));
407 CHECK(krb5_store_uint32(sp, ent->attributes));
408 CHECK(krb5_store_uint32(sp, ent->kvno));
409 CHECK(krb5_store_uint32(sp, ent->mkvno));
410 CHECK(store_string_xdr(sp, ent->policy));
411 CHECK(krb5_store_int32(sp, ent->aux_attributes));
412 CHECK(krb5_store_int32(sp, ent->max_renewable_life));
413 CHECK(krb5_store_int32(sp, ent->last_success));
414 CHECK(krb5_store_int32(sp, ent->last_failed));
415 CHECK(krb5_store_int32(sp, ent->fail_auth_count));
416 CHECK(krb5_store_int32(sp, ent->n_key_data));
417 CHECK(krb5_store_int32(sp, ent->n_tl_data));
418 CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
419 if (ent->n_tl_data) {
420 krb5_tl_data *tp;
422 for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
423 krb5_data c;
424 c.length = tp->tl_data_length;
425 c.data = tp->tl_data_contents;
427 CHECK(krb5_store_int32(sp, 0)); /* last item */
428 CHECK(krb5_store_int32(sp, tp->tl_data_type));
429 CHECK(store_data_xdr(sp, c));
431 CHECK(krb5_store_int32(sp, 1)); /* last item */
434 CHECK(krb5_store_int32(sp, ent->n_key_data));
435 for (i = 0; i < ent->n_key_data; i++) {
436 CHECK(krb5_store_uint32(sp, 2));
437 CHECK(krb5_store_uint32(sp, ent->kvno));
438 CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
439 CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
442 return 0;
445 static int
446 ret_principal_ent(krb5_context contextp,
447 krb5_storage *sp,
448 kadm5_principal_ent_rec *ent)
450 uint32_t flag, num;
451 size_t i;
453 memset(ent, 0, sizeof(*ent));
455 CHECK(ret_principal_xdr(contextp, sp, &ent->principal));
456 CHECK(krb5_ret_uint32(sp, &flag));
457 ent->princ_expire_time = flag;
458 CHECK(krb5_ret_uint32(sp, &flag));
459 ent->pw_expiration = flag;
460 CHECK(krb5_ret_uint32(sp, &flag));
461 ent->last_pwd_change = flag;
462 CHECK(krb5_ret_uint32(sp, &flag));
463 ent->max_life = flag;
464 CHECK(krb5_ret_uint32(sp, &flag));
465 if (flag == 0)
466 CHECK(ret_principal_xdr(contextp, sp, &ent->mod_name));
467 CHECK(krb5_ret_uint32(sp, &flag));
468 ent->mod_date = flag;
469 CHECK(krb5_ret_uint32(sp, &flag));
470 ent->attributes = flag;
471 CHECK(krb5_ret_uint32(sp, &flag));
472 ent->kvno = flag;
473 CHECK(krb5_ret_uint32(sp, &flag));
474 ent->mkvno = flag;
475 CHECK(ret_string_xdr(sp, &ent->policy));
476 CHECK(krb5_ret_uint32(sp, &flag));
477 ent->aux_attributes = flag;
478 CHECK(krb5_ret_uint32(sp, &flag));
479 ent->max_renewable_life = flag;
480 CHECK(krb5_ret_uint32(sp, &flag));
481 ent->last_success = flag;
482 CHECK(krb5_ret_uint32(sp, &flag));
483 ent->last_failed = flag;
484 CHECK(krb5_ret_uint32(sp, &flag));
485 ent->fail_auth_count = flag;
486 CHECK(krb5_ret_uint32(sp, &flag));
487 ent->n_key_data = flag;
488 CHECK(krb5_ret_uint32(sp, &flag));
489 ent->n_tl_data = flag;
490 CHECK(krb5_ret_uint32(sp, &flag));
491 if (flag == 0) {
492 krb5_tl_data **tp = &ent->tl_data;
493 size_t count = 0;
495 while(1) {
496 krb5_data c;
497 CHECK(krb5_ret_uint32(sp, &flag)); /* last item */
498 if (flag)
499 break;
500 *tp = calloc(1, sizeof(**tp));
501 INSIST(*tp != NULL);
502 CHECK(krb5_ret_uint32(sp, &flag));
503 (*tp)->tl_data_type = flag;
504 CHECK(ret_data_xdr(sp, &c));
505 (*tp)->tl_data_length = c.length;
506 (*tp)->tl_data_contents = c.data;
507 tp = &(*tp)->tl_data_next;
509 count++;
511 INSIST((size_t)ent->n_tl_data == count);
512 } else {
513 INSIST(ent->n_tl_data == 0);
516 CHECK(krb5_ret_uint32(sp, &num));
517 INSIST(num == (uint32_t)ent->n_key_data);
519 ent->key_data = calloc(num, sizeof(ent->key_data[0]));
520 INSIST(ent->key_data != NULL);
522 for (i = 0; i < num; i++) {
523 CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
524 INSIST(flag > 1);
525 CHECK(krb5_ret_uint32(sp, &flag));
526 ent->kvno = flag;
527 CHECK(krb5_ret_uint32(sp, &flag));
528 ent->key_data[i].key_data_type[0] = flag;
529 CHECK(krb5_ret_uint32(sp, &flag));
530 ent->key_data[i].key_data_type[1] = flag;
532 CHECK(i == num);
534 return 0;
541 static void
542 proc_create_principal(kadm5_server_context *contextp,
543 krb5_storage *in,
544 krb5_storage *out)
546 uint32_t version, mask;
547 kadm5_principal_ent_rec ent;
548 krb5_error_code ret;
549 char *password;
551 memset(&ent, 0, sizeof(ent));
553 CHECK(krb5_ret_uint32(in, &version));
554 INSIST(version == VERSION2);
555 CHECK(ret_principal_ent(contextp->context, in, &ent));
556 CHECK(krb5_ret_uint32(in, &mask));
557 CHECK(ret_string_xdr(in, &password));
559 INSIST(ent.principal);
562 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal);
563 if (ret)
564 goto fail;
566 ret = kadm5_create_principal(contextp, &ent, mask, password);
568 fail:
569 krb5_warn(contextp->context, ret, "create principal");
570 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
571 CHECK(krb5_store_uint32(out, ret)); /* code */
573 free(password);
574 kadm5_free_principal_ent(contextp, &ent);
577 static void
578 proc_delete_principal(kadm5_server_context *contextp,
579 krb5_storage *in,
580 krb5_storage *out)
582 uint32_t version;
583 krb5_principal princ;
584 krb5_error_code ret;
586 CHECK(krb5_ret_uint32(in, &version));
587 INSIST(version == VERSION2);
588 CHECK(ret_principal_xdr(contextp->context, in, &princ));
590 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
591 if (ret)
592 goto fail;
594 ret = kadm5_delete_principal(contextp, princ);
596 fail:
597 krb5_warn(contextp->context, ret, "delete principal");
598 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
599 CHECK(krb5_store_uint32(out, ret)); /* code */
601 krb5_free_principal(contextp->context, princ);
604 static void
605 proc_get_principal(kadm5_server_context *contextp,
606 krb5_storage *in,
607 krb5_storage *out)
609 uint32_t version, mask;
610 krb5_principal princ;
611 kadm5_principal_ent_rec ent;
612 krb5_error_code ret;
614 memset(&ent, 0, sizeof(ent));
616 CHECK(krb5_ret_uint32(in, &version));
617 INSIST(version == VERSION2);
618 CHECK(ret_principal_xdr(contextp->context, in, &princ));
619 CHECK(krb5_ret_uint32(in, &mask));
621 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
622 if(ret)
623 goto fail;
625 ret = kadm5_get_principal(contextp, princ, &ent, mask);
627 fail:
628 krb5_warn(contextp->context, ret, "get principal principal");
630 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
631 CHECK(krb5_store_uint32(out, ret)); /* code */
632 if (ret == 0) {
633 CHECK(store_principal_ent(contextp->context, out, &ent));
635 krb5_free_principal(contextp->context, princ);
636 kadm5_free_principal_ent(contextp, &ent);
639 static void
640 proc_chrand_principal_v2(kadm5_server_context *contextp,
641 krb5_storage *in,
642 krb5_storage *out)
644 krb5_error_code ret;
645 krb5_principal princ;
646 uint32_t version;
647 krb5_keyblock *new_keys;
648 int n_keys;
650 CHECK(krb5_ret_uint32(in, &version));
651 INSIST(version == VERSION2);
652 CHECK(ret_principal_xdr(contextp->context, in, &princ));
654 ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
655 if(ret)
656 goto fail;
658 ret = kadm5_randkey_principal(contextp, princ,
659 &new_keys, &n_keys);
661 fail:
662 krb5_warn(contextp->context, ret, "rand key principal");
664 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
665 CHECK(krb5_store_uint32(out, ret));
666 if (ret == 0) {
667 int i;
668 CHECK(krb5_store_int32(out, n_keys));
670 for(i = 0; i < n_keys; i++){
671 CHECK(krb5_store_uint32(out, new_keys[i].keytype));
672 CHECK(store_data_xdr(out, new_keys[i].keyvalue));
673 krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
675 free(new_keys);
677 krb5_free_principal(contextp->context, princ);
680 static void
681 proc_init(kadm5_server_context *contextp,
682 krb5_storage *in,
683 krb5_storage *out)
685 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
686 CHECK(krb5_store_uint32(out, 0)); /* code */
687 CHECK(krb5_store_uint32(out, 0)); /* code */
690 struct krb5_proc {
691 const char *name;
692 void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
693 } rwprocs[] = {
694 { "NULL", NULL },
695 { "create principal", proc_create_principal },
696 { "delete principal", proc_delete_principal },
697 { "modify principal", NULL },
698 { "rename principal", NULL },
699 { "get principal", proc_get_principal },
700 { "chpass principal", NULL },
701 { "chrand principal", proc_chrand_principal_v2 },
702 { "create policy", NULL },
703 { "delete policy", NULL },
704 { "modify policy", NULL },
705 { "get policy", NULL },
706 { "get privs", NULL },
707 { "init", proc_init },
708 { "get principals", NULL },
709 { "get polices", NULL },
710 { "setkey principal", NULL },
711 { "setkey principal v4", NULL },
712 { "create principal v3", NULL },
713 { "chpass principal v3", NULL },
714 { "chrand principal v3", NULL },
715 { "setkey principal v3", NULL }
716 }, roprocs[] = {
717 { "NULL", NULL },
718 { "create principal", NULL },
719 { "delete principal", NULL },
720 { "modify principal", NULL },
721 { "rename principal", NULL },
722 { "get principal", proc_get_principal },
723 { "chpass principal", NULL },
724 { "chrand principal", NULL },
725 { "create policy", NULL },
726 { "delete policy", NULL },
727 { "modify policy", NULL },
728 { "get policy", NULL },
729 { "get privs", NULL },
730 { "init", NULL },
731 { "get principals", NULL },
732 { "get polices", NULL },
733 { "setkey principal", NULL },
734 { "setkey principal v4", NULL },
735 { "create principal v3", NULL },
736 { "chpass principal v3", NULL },
737 { "chrand principal v3", NULL },
738 { "setkey principal v3", NULL }
741 static krb5_error_code
742 copyheader(krb5_storage *sp, krb5_data *data)
744 off_t off;
745 ssize_t sret;
747 off = krb5_storage_seek(sp, 0, SEEK_CUR);
749 CHECK(krb5_data_alloc(data, off));
750 INSIST((size_t)off == data->length);
751 krb5_storage_seek(sp, 0, SEEK_SET);
752 sret = krb5_storage_read(sp, data->data, data->length);
753 INSIST(sret == off);
754 INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
756 return 0;
759 struct gctx {
760 krb5_data handle;
761 gss_ctx_id_t ctx;
762 uint32_t seq_num;
763 int done;
764 int inprogress;
767 static int
768 process_stream(krb5_context contextp,
769 unsigned char *buf,
770 size_t ilen,
771 krb5_storage *sp,
772 int readonly)
774 krb5_error_code ret;
775 krb5_storage *msg, *reply, *dreply;
776 OM_uint32 maj_stat, min_stat;
777 gss_buffer_desc gin, gout;
778 struct gctx gctx;
779 void *server_handle = NULL;
781 memset(&gctx, 0, sizeof(gctx));
783 msg = krb5_storage_emem();
784 reply = krb5_storage_emem();
785 dreply = krb5_storage_emem();
787 if (msg == NULL || reply == NULL || dreply == NULL) {
788 if (msg != NULL)
789 krb5_storage_free(msg);
790 if (reply != NULL)
791 krb5_storage_free(reply);
792 if (dreply != NULL)
793 krb5_storage_free(dreply);
794 return krb5_enomem(contextp);
798 * First packet comes partly from the caller
801 INSIST(ilen >= 4);
803 while (1) {
804 struct call_header chdr;
805 struct gcred gcred;
806 uint32_t mtype;
807 krb5_data headercopy;
809 krb5_storage_truncate(dreply, 0);
810 krb5_storage_truncate(reply, 0);
811 krb5_storage_truncate(msg, 0);
813 krb5_data_zero(&headercopy);
814 memset(&chdr, 0, sizeof(chdr));
815 memset(&gcred, 0, sizeof(gcred));
818 * This is very icky to handle the the auto-detection between
819 * the Heimdal protocol and the MIT ONC-RPC based protocol.
822 if (ilen) {
823 int last_fragment;
824 unsigned long len;
825 ssize_t slen;
826 unsigned char tmp[4];
828 if (ilen < 4) {
829 memcpy(tmp, buf, ilen);
830 slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
831 INSIST((size_t)slen == sizeof(tmp) - ilen);
833 ilen = sizeof(tmp);
834 buf = tmp;
836 INSIST(ilen >= 4);
838 _krb5_get_int(buf, &len, 4);
839 last_fragment = (len & LAST_FRAGMENT) != 0;
840 len &= ~LAST_FRAGMENT;
842 ilen -= 4;
843 buf += 4;
845 if (ilen) {
846 if (len < ilen) {
847 slen = krb5_storage_write(msg, buf, len);
848 INSIST((size_t)slen == len);
849 ilen -= len;
850 len = 0;
851 } else {
852 slen = krb5_storage_write(msg, buf, ilen);
853 INSIST((size_t)slen == ilen);
854 len -= ilen;
858 CHECK(read_data(sp, msg, len));
860 if (!last_fragment) {
861 ret = collect_framents(sp, msg);
862 if (ret == HEIM_ERR_EOF)
863 krb5_errx(contextp, 0, "client disconnected");
864 INSIST(ret == 0);
866 } else {
868 ret = collect_framents(sp, msg);
869 if (ret == HEIM_ERR_EOF)
870 krb5_errx(contextp, 0, "client disconnected");
871 INSIST(ret == 0);
873 krb5_storage_seek(msg, 0, SEEK_SET);
875 CHECK(krb5_ret_uint32(msg, &chdr.xid));
876 CHECK(krb5_ret_uint32(msg, &mtype));
877 CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
878 CHECK(krb5_ret_uint32(msg, &chdr.prog));
879 CHECK(krb5_ret_uint32(msg, &chdr.vers));
880 CHECK(krb5_ret_uint32(msg, &chdr.proc));
881 CHECK(ret_auth_opaque(msg, &chdr.cred));
882 CHECK(copyheader(msg, &headercopy));
883 CHECK(ret_auth_opaque(msg, &chdr.verf));
885 INSIST(chdr.rpcvers == RPC_VERSION);
886 INSIST(chdr.prog == KADM_SERVER);
887 INSIST(chdr.vers == VVERSION);
888 INSIST(chdr.cred.flavor == FLAVOR_GSS);
890 CHECK(ret_gcred(&chdr.cred.data, &gcred));
892 INSIST(gcred.version == FLAVOR_GSS_VERSION);
894 if (gctx.done) {
895 INSIST(chdr.verf.flavor == FLAVOR_GSS);
897 /* from first byte to last of credential */
898 gin.value = headercopy.data;
899 gin.length = headercopy.length;
900 gout.value = chdr.verf.data.data;
901 gout.length = chdr.verf.data.length;
903 maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
904 INSIST(maj_stat == GSS_S_COMPLETE);
907 switch(gcred.proc) {
908 case RPG_DATA: {
909 krb5_data data;
910 int conf_state;
911 uint32_t seq;
912 krb5_storage *sp1;
913 struct krb5_proc *procs = readonly ? roprocs : rwprocs;
915 INSIST(gcred.service == rpg_privacy);
917 INSIST(gctx.done);
919 INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
921 CHECK(ret_data_xdr(msg, &data));
923 gin.value = data.data;
924 gin.length = data.length;
926 maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
927 &conf_state, NULL);
928 krb5_data_free(&data);
929 INSIST(maj_stat == GSS_S_COMPLETE);
930 INSIST(conf_state != 0);
932 sp1 = krb5_storage_from_mem(gout.value, gout.length);
933 INSIST(sp1 != NULL);
935 CHECK(krb5_ret_uint32(sp1, &seq));
936 INSIST (seq == gcred.seq_num);
939 * Check sequence number
941 INSIST(seq > gctx.seq_num);
942 gctx.seq_num = seq;
945 * If contextp is setup, priv data have the seq_num stored
946 * first in the block, so add it here before users data is
947 * added.
949 CHECK(krb5_store_uint32(dreply, gctx.seq_num));
951 if (chdr.proc >= sizeof(rwprocs)/sizeof(rwprocs[0])) {
952 krb5_warnx(contextp, "proc number out of array");
953 } else if (procs[chdr.proc].func == NULL) {
954 if (readonly && rwprocs[chdr.proc].func)
955 krb5_warnx(contextp,
956 "proc '%s' not allowed (readonly mode)",
957 procs[chdr.proc].name);
958 else
959 krb5_warnx(contextp, "proc '%s' never implemented",
960 procs[chdr.proc].name);
961 } else {
962 krb5_warnx(contextp, "proc %s", procs[chdr.proc].name);
963 INSIST(server_handle != NULL);
964 (*procs[chdr.proc].func)(server_handle, sp, dreply);
966 krb5_storage_free(sp);
967 gss_release_buffer(&min_stat, &gout);
969 break;
971 case RPG_INIT:
972 INSIST(gctx.inprogress == 0);
973 INSIST(gctx.ctx == NULL);
975 gctx.inprogress = 1;
976 HEIM_FALLTHROUGH;
977 case RPG_CONTINUE_INIT: {
978 gss_name_t src_name = GSS_C_NO_NAME;
979 krb5_data in;
981 INSIST(gctx.inprogress);
983 CHECK(ret_data_xdr(msg, &in));
985 gin.value = in.data;
986 gin.length = in.length;
987 gout.value = NULL;
988 gout.length = 0;
990 maj_stat = gss_accept_sec_context(&min_stat,
991 &gctx.ctx,
992 GSS_C_NO_CREDENTIAL,
993 &gin,
994 GSS_C_NO_CHANNEL_BINDINGS,
995 &src_name,
996 NULL,
997 &gout,
998 NULL,
999 NULL,
1000 NULL);
1001 if (GSS_ERROR(maj_stat)) {
1002 gss_print_errors(contextp, maj_stat, min_stat);
1003 krb5_errx(contextp, 1, "gss error, exit");
1005 if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
1006 kadm5_config_params realm_params;
1007 gss_buffer_desc bufp;
1008 char *client;
1010 gctx.done = 1;
1012 memset(&realm_params, 0, sizeof(realm_params));
1014 maj_stat = gss_export_name(&min_stat, src_name, &bufp);
1015 INSIST(maj_stat == GSS_S_COMPLETE);
1017 CHECK(parse_name(bufp.value, bufp.length,
1018 GSS_KRB5_MECHANISM, &client));
1020 gss_release_buffer(&min_stat, &bufp);
1022 krb5_warnx(contextp, "%s connected", client);
1024 ret = kadm5_s_init_with_password_ctx(contextp,
1025 client,
1026 NULL,
1027 KADM5_ADMIN_SERVICE,
1028 &realm_params,
1029 0, 0,
1030 &server_handle);
1031 INSIST(ret == 0);
1034 INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
1036 CHECK(krb5_store_uint32(dreply, 0));
1037 CHECK(store_gss_init_res(dreply, gctx.handle,
1038 maj_stat, min_stat, 1, &gout));
1039 if (gout.value)
1040 gss_release_buffer(&min_stat, &gout);
1041 if (src_name)
1042 gss_release_name(&min_stat, &src_name);
1044 break;
1046 case RPG_DESTROY:
1047 krb5_errx(contextp, 1, "client destroyed gss contextp");
1048 default:
1049 krb5_errx(contextp, 1, "client sent unknown gsscode %d",
1050 (int)gcred.proc);
1053 krb5_data_free(&gcred.handle);
1054 krb5_data_free(&chdr.cred.data);
1055 krb5_data_free(&chdr.verf.data);
1056 krb5_data_free(&headercopy);
1058 CHECK(krb5_store_uint32(reply, chdr.xid));
1059 CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1060 CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1062 if (!gctx.done) {
1063 krb5_data data;
1065 CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1066 CHECK(krb5_store_uint32(reply, 0)); /* length */
1068 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1070 CHECK(krb5_storage_to_data(dreply, &data));
1071 INSIST((size_t)krb5_storage_write(reply, data.data, data.length) == data.length);
1072 krb5_data_free(&data);
1074 } else {
1075 uint32_t seqnum = htonl(gctx.seq_num);
1076 krb5_data data;
1078 gin.value = &seqnum;
1079 gin.length = sizeof(seqnum);
1081 maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
1082 INSIST(maj_stat == GSS_S_COMPLETE);
1084 data.data = gout.value;
1085 data.length = gout.length;
1087 CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
1088 CHECK(store_data_xdr(reply, data));
1089 gss_release_buffer(&min_stat, &gout);
1091 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1093 CHECK(krb5_storage_to_data(dreply, &data));
1095 if (gctx.inprogress) {
1096 ssize_t sret;
1097 gctx.inprogress = 0;
1098 sret = krb5_storage_write(reply, data.data, data.length);
1099 INSIST((size_t)sret == data.length);
1100 krb5_data_free(&data);
1101 } else {
1102 int conf_state;
1104 gin.value = data.data;
1105 gin.length = data.length;
1107 maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
1108 &gin, &conf_state, &gout);
1109 INSIST(maj_stat == GSS_S_COMPLETE);
1110 INSIST(conf_state != 0);
1111 krb5_data_free(&data);
1113 data.data = gout.value;
1114 data.length = gout.length;
1116 store_data_xdr(reply, data);
1117 gss_release_buffer(&min_stat, &gout);
1122 krb5_data data;
1123 ssize_t sret;
1124 CHECK(krb5_storage_to_data(reply, &data));
1125 CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
1126 sret = krb5_storage_write(sp, data.data, data.length);
1127 INSIST((size_t)sret == data.length);
1128 krb5_data_free(&data);
1136 handle_mit(krb5_context contextp,
1137 void *buf,
1138 size_t len,
1139 krb5_socket_t sock,
1140 int readonly)
1142 krb5_storage *sp;
1144 dcontext = contextp;
1146 sp = krb5_storage_from_socket(sock);
1147 INSIST(sp != NULL);
1149 process_stream(contextp, buf, len, sp, readonly);
1151 return 0;