Use strerror_r
[heimdal.git] / kadmin / rpc.c
blob9170d68caebed659b4462f4f9722e5369728ac67
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 = 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 context,
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(context, "%.*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 context,
188 OM_uint32 maj_stat, OM_uint32 min_stat)
190 gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
191 gss_error(context, 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(slen == tlen);
209 slen = krb5_storage_write(msg, buf, tlen);
210 INSIST(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(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(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 context,
366 krb5_storage *sp,
367 krb5_principal p)
369 char *str;
370 CHECK(krb5_unparse_name(context, p, &str));
371 CHECK(store_string_xdr(sp, str));
372 free(str);
373 return 0;
376 static int
377 ret_principal_xdr(krb5_context context,
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(context, str, p));
386 free(str);
388 return 0;
391 static int
392 store_principal_ent(krb5_context context,
393 krb5_storage *sp,
394 kadm5_principal_ent_rec *ent)
396 size_t i;
398 CHECK(store_principal_xdr(context, 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(context, 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 context,
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(context, 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 ret_principal_xdr(context, 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(ent->n_tl_data == count);
512 } else {
513 INSIST(ent->n_tl_data == 0);
516 CHECK(krb5_ret_uint32(sp, &num));
517 INSIST(num == 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;
533 return 0;
540 static void
541 proc_create_principal(kadm5_server_context *context,
542 krb5_storage *in,
543 krb5_storage *out)
545 uint32_t version, mask;
546 kadm5_principal_ent_rec ent;
547 krb5_error_code ret;
548 char *password;
550 memset(&ent, 0, sizeof(ent));
552 CHECK(krb5_ret_uint32(in, &version));
553 INSIST(version == VERSION2);
554 CHECK(ret_principal_ent(context->context, in, &ent));
555 CHECK(krb5_ret_uint32(in, &mask));
556 CHECK(ret_string_xdr(in, &password));
558 INSIST(ent.principal);
561 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD, ent.principal);
562 if (ret)
563 goto fail;
565 ret = kadm5_create_principal(context, &ent, mask, password);
567 fail:
568 krb5_warn(context->context, ret, "create principal");
569 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
570 CHECK(krb5_store_uint32(out, ret)); /* code */
572 free(password);
573 kadm5_free_principal_ent(context, &ent);
576 static void
577 proc_delete_principal(kadm5_server_context *context,
578 krb5_storage *in,
579 krb5_storage *out)
581 uint32_t version;
582 krb5_principal princ;
583 krb5_error_code ret;
585 CHECK(krb5_ret_uint32(in, &version));
586 INSIST(version == VERSION2);
587 CHECK(ret_principal_xdr(context->context, in, &princ));
589 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ);
590 if (ret)
591 goto fail;
593 ret = kadm5_delete_principal(context, princ);
595 fail:
596 krb5_warn(context->context, ret, "delete principal");
597 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
598 CHECK(krb5_store_uint32(out, ret)); /* code */
600 krb5_free_principal(context->context, princ);
603 static void
604 proc_get_principal(kadm5_server_context *context,
605 krb5_storage *in,
606 krb5_storage *out)
608 uint32_t version, mask;
609 krb5_principal princ;
610 kadm5_principal_ent_rec ent;
611 krb5_error_code ret;
613 memset(&ent, 0, sizeof(ent));
615 CHECK(krb5_ret_uint32(in, &version));
616 INSIST(version == VERSION2);
617 CHECK(ret_principal_xdr(context->context, in, &princ));
618 CHECK(krb5_ret_uint32(in, &mask));
620 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ);
621 if(ret)
622 goto fail;
624 ret = kadm5_get_principal(context, princ, &ent, mask);
626 fail:
627 krb5_warn(context->context, ret, "get principal principal");
629 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
630 CHECK(krb5_store_uint32(out, ret)); /* code */
631 if (ret == 0) {
632 CHECK(store_principal_ent(context->context, out, &ent));
634 krb5_free_principal(context->context, princ);
635 kadm5_free_principal_ent(context, &ent);
638 static void
639 proc_chrand_principal_v2(kadm5_server_context *context,
640 krb5_storage *in,
641 krb5_storage *out)
643 krb5_error_code ret;
644 krb5_principal princ;
645 uint32_t version;
646 krb5_keyblock *new_keys;
647 int n_keys;
649 CHECK(krb5_ret_uint32(in, &version));
650 INSIST(version == VERSION2);
651 CHECK(ret_principal_xdr(context->context, in, &princ));
653 ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ);
654 if(ret)
655 goto fail;
657 ret = kadm5_randkey_principal(context, princ,
658 &new_keys, &n_keys);
660 fail:
661 krb5_warn(context->context, ret, "rand key principal");
663 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
664 CHECK(krb5_store_uint32(out, ret));
665 if (ret == 0) {
666 size_t i;
667 CHECK(krb5_store_int32(out, n_keys));
669 for(i = 0; i < n_keys; i++){
670 CHECK(krb5_store_uint32(out, new_keys[i].keytype));
671 CHECK(store_data_xdr(out, new_keys[i].keyvalue));
672 krb5_free_keyblock_contents(context->context, &new_keys[i]);
674 free(new_keys);
676 krb5_free_principal(context->context, princ);
679 static void
680 proc_init(kadm5_server_context *context,
681 krb5_storage *in,
682 krb5_storage *out)
684 CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
685 CHECK(krb5_store_uint32(out, 0)); /* code */
686 CHECK(krb5_store_uint32(out, 0)); /* code */
689 struct proc {
690 char *name;
691 void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
692 } procs[] = {
693 { "NULL", NULL },
694 { "create principal", proc_create_principal },
695 { "delete principal", proc_delete_principal },
696 { "modify principal", NULL },
697 { "rename principal", NULL },
698 { "get principal", proc_get_principal },
699 { "chpass principal", NULL },
700 { "chrand principal", proc_chrand_principal_v2 },
701 { "create policy", NULL },
702 { "delete policy", NULL },
703 { "modify policy", NULL },
704 { "get policy", NULL },
705 { "get privs", NULL },
706 { "init", proc_init },
707 { "get principals", NULL },
708 { "get polices", NULL },
709 { "setkey principal", NULL },
710 { "setkey principal v4", NULL },
711 { "create principal v3", NULL },
712 { "chpass principal v3", NULL },
713 { "chrand principal v3", NULL },
714 { "setkey principal v3", NULL }
717 static krb5_error_code
718 copyheader(krb5_storage *sp, krb5_data *data)
720 off_t off;
721 ssize_t sret;
723 off = krb5_storage_seek(sp, 0, SEEK_CUR);
725 CHECK(krb5_data_alloc(data, off));
726 INSIST(off == data->length);
727 krb5_storage_seek(sp, 0, SEEK_SET);
728 sret = krb5_storage_read(sp, data->data, data->length);
729 INSIST(sret == off);
730 INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
732 return 0;
735 struct gctx {
736 krb5_data handle;
737 gss_ctx_id_t ctx;
738 uint32_t seq_num;
739 int done;
740 int inprogress;
743 static int
744 process_stream(krb5_context context,
745 unsigned char *buf, size_t ilen,
746 krb5_storage *sp)
748 krb5_error_code ret;
749 krb5_storage *msg, *reply, *dreply;
750 OM_uint32 maj_stat, min_stat;
751 gss_buffer_desc gin, gout;
752 struct gctx gctx;
753 void *server_handle = NULL;
755 memset(&gctx, 0, sizeof(gctx));
757 msg = krb5_storage_emem();
758 reply = krb5_storage_emem();
759 dreply = krb5_storage_emem();
762 * First packet comes partly from the caller
765 INSIST(ilen >= 4);
767 while (1) {
768 struct call_header chdr;
769 struct gcred gcred;
770 uint32_t mtype;
771 krb5_data headercopy;
773 krb5_storage_truncate(dreply, 0);
774 krb5_storage_truncate(reply, 0);
775 krb5_storage_truncate(msg, 0);
777 krb5_data_zero(&headercopy);
778 memset(&chdr, 0, sizeof(chdr));
779 memset(&gcred, 0, sizeof(gcred));
782 * This is very icky to handle the the auto-detection between
783 * the Heimdal protocol and the MIT ONC-RPC based protocol.
786 if (ilen) {
787 int last_fragment;
788 unsigned long len;
789 ssize_t slen;
790 unsigned char tmp[4];
792 if (ilen < 4) {
793 memcpy(tmp, buf, ilen);
794 slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
795 INSIST(slen == sizeof(tmp) - ilen);
797 ilen = sizeof(tmp);
798 buf = tmp;
800 INSIST(ilen >= 4);
802 _krb5_get_int(buf, &len, 4);
803 last_fragment = (len & LAST_FRAGMENT) != 0;
804 len &= ~LAST_FRAGMENT;
806 ilen -= 4;
807 buf += 4;
809 if (ilen) {
810 if (len < ilen) {
811 slen = krb5_storage_write(msg, buf, len);
812 INSIST(slen == len);
813 ilen -= len;
814 len = 0;
815 } else {
816 slen = krb5_storage_write(msg, buf, ilen);
817 INSIST(slen == ilen);
818 len -= ilen;
822 CHECK(read_data(sp, msg, len));
824 if (!last_fragment) {
825 ret = collect_framents(sp, msg);
826 if (ret == HEIM_ERR_EOF)
827 krb5_errx(context, 0, "client disconnected");
828 INSIST(ret == 0);
830 } else {
832 ret = collect_framents(sp, msg);
833 if (ret == HEIM_ERR_EOF)
834 krb5_errx(context, 0, "client disconnected");
835 INSIST(ret == 0);
837 krb5_storage_seek(msg, 0, SEEK_SET);
839 CHECK(krb5_ret_uint32(msg, &chdr.xid));
840 CHECK(krb5_ret_uint32(msg, &mtype));
841 CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
842 CHECK(krb5_ret_uint32(msg, &chdr.prog));
843 CHECK(krb5_ret_uint32(msg, &chdr.vers));
844 CHECK(krb5_ret_uint32(msg, &chdr.proc));
845 CHECK(ret_auth_opaque(msg, &chdr.cred));
846 CHECK(copyheader(msg, &headercopy));
847 CHECK(ret_auth_opaque(msg, &chdr.verf));
849 INSIST(chdr.rpcvers == RPC_VERSION);
850 INSIST(chdr.prog == KADM_SERVER);
851 INSIST(chdr.vers == VVERSION);
852 INSIST(chdr.cred.flavor == FLAVOR_GSS);
854 CHECK(ret_gcred(&chdr.cred.data, &gcred));
856 INSIST(gcred.version == FLAVOR_GSS_VERSION);
858 if (gctx.done) {
859 INSIST(chdr.verf.flavor == FLAVOR_GSS);
861 /* from first byte to last of credential */
862 gin.value = headercopy.data;
863 gin.length = headercopy.length;
864 gout.value = chdr.verf.data.data;
865 gout.length = chdr.verf.data.length;
867 maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
868 INSIST(maj_stat == GSS_S_COMPLETE);
871 switch(gcred.proc) {
872 case RPG_DATA: {
873 krb5_data data;
874 int conf_state;
875 uint32_t seq;
876 krb5_storage *sp;
878 INSIST(gcred.service == rpg_privacy);
880 INSIST(gctx.done);
882 INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
884 CHECK(ret_data_xdr(msg, &data));
886 gin.value = data.data;
887 gin.length = data.length;
889 maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
890 &conf_state, NULL);
891 krb5_data_free(&data);
892 INSIST(maj_stat == GSS_S_COMPLETE);
893 INSIST(conf_state != 0);
895 sp = krb5_storage_from_mem(gout.value, gout.length);
896 INSIST(sp != NULL);
898 CHECK(krb5_ret_uint32(sp, &seq));
899 INSIST (seq == gcred.seq_num);
902 * Check sequence number
904 INSIST(seq > gctx.seq_num);
905 gctx.seq_num = seq;
908 * If context is setup, priv data have the seq_num stored
909 * first in the block, so add it here before users data is
910 * added.
912 CHECK(krb5_store_uint32(dreply, gctx.seq_num));
914 if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) {
915 krb5_warnx(context, "proc number out of array");
916 } else if (procs[chdr.proc].func == NULL) {
917 krb5_warnx(context, "proc '%s' never implemented",
918 procs[chdr.proc].name);
919 } else {
920 krb5_warnx(context, "proc %s", procs[chdr.proc].name);
921 INSIST(server_handle != NULL);
922 (*procs[chdr.proc].func)(server_handle, sp, dreply);
924 krb5_storage_free(sp);
925 gss_release_buffer(&min_stat, &gout);
927 break;
929 case RPG_INIT:
930 INSIST(gctx.inprogress == 0);
931 INSIST(gctx.ctx == NULL);
933 gctx.inprogress = 1;
934 /* FALL THOUGH */
935 case RPG_CONTINUE_INIT: {
936 gss_name_t src_name = GSS_C_NO_NAME;
937 krb5_data in;
939 INSIST(gctx.inprogress);
941 CHECK(ret_data_xdr(msg, &in));
943 gin.value = in.data;
944 gin.length = in.length;
945 gout.value = NULL;
946 gout.length = 0;
948 maj_stat = gss_accept_sec_context(&min_stat,
949 &gctx.ctx,
950 GSS_C_NO_CREDENTIAL,
951 &gin,
952 GSS_C_NO_CHANNEL_BINDINGS,
953 &src_name,
954 NULL,
955 &gout,
956 NULL,
957 NULL,
958 NULL);
959 if (GSS_ERROR(maj_stat)) {
960 gss_print_errors(context, maj_stat, min_stat);
961 krb5_errx(context, 1, "gss error, exit");
963 if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
964 kadm5_config_params realm_params;
965 gss_buffer_desc buf;
966 char *client;
968 gctx.done = 1;
970 memset(&realm_params, 0, sizeof(realm_params));
972 maj_stat = gss_export_name(&min_stat, src_name, &buf);
973 INSIST(maj_stat == GSS_S_COMPLETE);
975 CHECK(parse_name(buf.value, buf.length,
976 GSS_KRB5_MECHANISM, &client));
978 gss_release_buffer(&min_stat, &buf);
980 krb5_warnx(context, "%s connected", client);
982 ret = kadm5_s_init_with_password_ctx(context,
983 client,
984 NULL,
985 KADM5_ADMIN_SERVICE,
986 &realm_params,
987 0, 0,
988 &server_handle);
989 INSIST(ret == 0);
992 INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
994 CHECK(krb5_store_uint32(dreply, 0));
995 CHECK(store_gss_init_res(dreply, gctx.handle,
996 maj_stat, min_stat, 1, &gout));
997 if (gout.value)
998 gss_release_buffer(&min_stat, &gout);
999 if (src_name)
1000 gss_release_name(&min_stat, &src_name);
1002 break;
1004 case RPG_DESTROY:
1005 krb5_errx(context, 1, "client destroyed gss context");
1006 default:
1007 krb5_errx(context, 1, "client sent unknown gsscode %d",
1008 (int)gcred.proc);
1011 krb5_data_free(&gcred.handle);
1012 krb5_data_free(&chdr.cred.data);
1013 krb5_data_free(&chdr.verf.data);
1014 krb5_data_free(&headercopy);
1016 CHECK(krb5_store_uint32(reply, chdr.xid));
1017 CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1018 CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1020 if (!gctx.done) {
1021 krb5_data data;
1023 CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1024 CHECK(krb5_store_uint32(reply, 0)); /* length */
1026 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1028 CHECK(krb5_storage_to_data(dreply, &data));
1029 INSIST(krb5_storage_write(reply, data.data, data.length) == data.length);
1030 krb5_data_free(&data);
1032 } else {
1033 uint32_t seqnum = htonl(gctx.seq_num);
1034 krb5_data data;
1036 gin.value = &seqnum;
1037 gin.length = sizeof(seqnum);
1039 maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
1040 INSIST(maj_stat == GSS_S_COMPLETE);
1042 data.data = gout.value;
1043 data.length = gout.length;
1045 CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
1046 CHECK(store_data_xdr(reply, data));
1047 gss_release_buffer(&min_stat, &gout);
1049 CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1051 CHECK(krb5_storage_to_data(dreply, &data));
1053 if (gctx.inprogress) {
1054 ssize_t sret;
1055 gctx.inprogress = 0;
1056 sret = krb5_storage_write(reply, data.data, data.length);
1057 INSIST(sret == data.length);
1058 krb5_data_free(&data);
1059 } else {
1060 int conf_state;
1062 gin.value = data.data;
1063 gin.length = data.length;
1065 maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
1066 &gin, &conf_state, &gout);
1067 INSIST(maj_stat == GSS_S_COMPLETE);
1068 INSIST(conf_state != 0);
1069 krb5_data_free(&data);
1071 data.data = gout.value;
1072 data.length = gout.length;
1074 store_data_xdr(reply, data);
1075 gss_release_buffer(&min_stat, &gout);
1080 krb5_data data;
1081 ssize_t sret;
1082 CHECK(krb5_storage_to_data(reply, &data));
1083 CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
1084 sret = krb5_storage_write(sp, data.data, data.length);
1085 INSIST(sret == data.length);
1086 krb5_data_free(&data);
1094 handle_mit(krb5_context context, void *buf, size_t len, int fd)
1096 krb5_storage *sp;
1098 dcontext = context;
1100 sp = krb5_storage_from_fd(fd);
1101 INSIST(sp != NULL);
1103 process_stream(context, buf, len, sp);
1105 return 0;