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