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#include <kadm5/private.h>
36
37#include <gssapi.h>
38#include <gssapi_krb5.h>
39#include <gssapi_spnego.h>
40
41#define CHECK(x)						\
42    do {							\
43	int __r;						\
44	if ((__r = (x))) {					\
45	    krb5_errx(dcontext, 1, "Failed (%d) on %s:%d",	\
46		      __r, __FILE__, __LINE__);			\
47	}							\
48    } while(0)
49
50#define EXPECT(x,expected)						\
51    do {								\
52	if ((x) != (expected)) {					\
53	    krb5_errx(dcontext, 1,					\
54		      "Got %d, was not the expected %d at %s:%d",	\
55		      x, expected, __FILE__, __LINE__);			\
56	}								\
57    } while(0)
58
59#define EXPECT_EGT(x,expected)				\
60    do {						\
61	if ((x) < (expected)) {				\
62	    krb5_errx(dcontext, 1,			\
63		      "Got %d that is < %d at %s:%d",	\
64		      x, expected, __FILE__, __LINE__);	\
65	}						\
66    } while(0)
67
68static krb5_context dcontext;
69
70#define INSIST(x) CHECK(!(x))
71
72#define VERSION2 0x12345702
73
74#define LAST_FRAGMENT 0x80000000
75
76#define RPC_VERSION 2
77#define KADM_SERVER 2112
78#define VVERSION 2
79#define FLAVOR_GSS 6
80#define 	FLAVOR_GSS_VERSION 1
81#define 	SEQ_WINDOW_SIZE		1
82#define FLAVOR_GSS_OLD 300001
83#define 	FLAVOR_GSS_OLD_MIN_VERSION 3
84
85enum {
86    RPG_DATA = 0,
87    RPG_INIT = 1,
88    RPG_CONTINUE_INIT = 2,
89    RPG_DESTROY = 3
90};
91
92enum {
93    rpg_privacy = 3
94};
95
96/*
97  struct chrand_ret {
98  krb5_ui_4 api_version;
99  kadm5_ret_t ret;
100  int n_keys;
101  krb5_keyblock *keys;
102  };
103*/
104
105
106static int
107parse_name(const unsigned char *p, size_t len,
108	   const gss_OID oid, char **name)
109{
110    size_t l;
111
112    if (len < 4)
113	return 1;
114
115    /* TOK_ID */
116    if (memcmp(p, "\x04\x01", 2) != 0)
117	return 1;
118    len -= 2;
119    p += 2;
120
121    /* MECH_LEN */
122    l = (p[0] << 8) | p[1];
123    len -= 2;
124    p += 2;
125    if (l < 2 || len < l)
126	return 1;
127
128    /* oid wrapping */
129    if (p[0] != 6 || p[1] != l - 2)
130	return 1;
131    p += 2;
132    l -= 2;
133    len -= 2;
134
135    /* MECH */
136    if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
137	return 1;
138    len -= l;
139    p += l;
140
141    /* MECHNAME_LEN */
142    if (len < 4)
143	return 1;
144    l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
145    len -= 4;
146    p += 4;
147
148    /* MECH NAME */
149    if (len != l)
150	return 1;
151
152    *name = malloc(l + 1);
153    INSIST(*name != NULL);
154    memcpy(*name, p, l);
155    (*name)[l] = '\0';
156
157    return 0;
158}
159
160
161
162static void
163gss_error(krb5_context lcontext,
164	  gss_OID mech, OM_uint32 type, OM_uint32 error)
165{
166    OM_uint32 new_stat;
167    OM_uint32 msg_ctx = 0;
168    gss_buffer_desc status_string;
169    OM_uint32 ret;
170
171    do {
172	ret = gss_display_status (&new_stat,
173				  error,
174				  type,
175				  mech,
176				  &msg_ctx,
177				  &status_string);
178	krb5_warnx(lcontext, "%.*s",
179		   (int)status_string.length,
180		   (char *)status_string.value);
181	gss_release_buffer (&new_stat, &status_string);
182    } while (!GSS_ERROR(ret) && msg_ctx != 0);
183}
184
185static void
186gss_print_errors (krb5_context lcontext,
187		  OM_uint32 maj_stat, OM_uint32 min_stat)
188{
189    gss_error(lcontext, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
190    gss_error(lcontext, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
191}
192
193static int
194read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
195{
196    char buf[1024];
197
198    while (len) {
199	size_t tlen = len;
200	ssize_t slen;
201
202	if (tlen > sizeof(buf))
203	    tlen = sizeof(buf);
204
205	slen = krb5_storage_read(sp, buf, tlen);
206	INSIST(slen == tlen);
207
208	slen = krb5_storage_write(msg, buf, tlen);
209	INSIST(slen == tlen);
210
211	len -= tlen;
212    }
213    return 0;
214}
215
216static int
217collect_fragments(krb5_storage *sp, krb5_storage *msg)
218{
219    krb5_error_code ret;
220    uint32_t len;
221    int last_fragment;
222    size_t total_len = 0;
223
224    do {
225	ret = krb5_ret_uint32(sp, &len);
226	if (ret)
227	    return ret;
228
229	last_fragment = (len & LAST_FRAGMENT);
230	len &= ~LAST_FRAGMENT;
231
232	CHECK(read_data(sp, msg, len));
233	total_len += len;
234
235    } while(!last_fragment || total_len == 0);
236
237    return 0;
238}
239
240/*
241 *
242 */
243
244static void
245proc_create_principal(kadm5_server_context *lcontext,
246		      krb5_storage *in,
247		      krb5_storage *out)
248{
249    uint32_t version, mask;
250    kadm5_principal_ent_rec ent;
251    krb5_error_code ret;
252    char *password, *princ = NULL;
253
254    memset(&ent, 0, sizeof(ent));
255
256    CHECK(krb5_ret_uint32(in, &version));
257    EXPECT_EGT(version, VERSION2);
258    CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
259    INSIST(ent.principal != NULL);
260    CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ));
261    CHECK(krb5_ret_uint32(in, &mask));
262    CHECK(_kadm5_xdr_ret_string_xdr(in, &password));
263
264    INSIST(ent.principal);
265
266
267    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal);
268    if (ret)
269	goto fail;
270
271    ret = kadm5_create_principal(lcontext, &ent, mask, password);
272
273 fail:
274    krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>");
275    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
276    CHECK(krb5_store_uint32(out, ret)); /* code */
277
278    free(password);
279    free(princ);
280    kadm5_free_principal_ent(context, &ent);
281}
282
283/* get array of salt tuples */
284
285static krb5_key_salt_tuple *
286parse_ks_tuple(krb5_context lcontext, krb5_storage *in, uint32_t *n_ks_tuple)
287{
288    krb5_key_salt_tuple *tuples;
289    uint32_t n;
290
291    CHECK(krb5_ret_uint32(in, n_ks_tuple));
292    INSIST(*n_ks_tuple < 1000);
293
294    if (*n_ks_tuple == 0)
295	return NULL;
296
297    tuples = calloc(*n_ks_tuple, sizeof(tuples[0]));
298    INSIST(tuples != NULL);
299
300    for (n = 0; n < *n_ks_tuple; n++) {
301	int32_t enctype, salttype;
302	CHECK(krb5_ret_int32(in, &enctype));
303	CHECK(krb5_ret_int32(in, &salttype));
304
305	tuples[n].ks_enctype = (krb5_enctype)enctype;
306	tuples[n].ks_salttype = (krb5_enctype)salttype;
307    }
308    return tuples;
309}
310
311static void
312proc_create_principal3(kadm5_server_context *lcontext,
313		       krb5_storage *in,
314		       krb5_storage *out)
315{
316    uint32_t version, mask, n_ks_tuple;
317    kadm5_principal_ent_rec ent;
318    krb5_error_code ret;
319    char *password, *princ = NULL;
320    krb5_key_salt_tuple *ks_tuple;
321
322    memset(&ent, 0, sizeof(ent));
323
324    CHECK(krb5_ret_uint32(in, &version));
325    EXPECT_EGT(version, VERSION2);
326    CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
327    INSIST(ent.principal != NULL);
328    CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ));
329    CHECK(krb5_ret_uint32(in, &mask));
330    ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple);
331    CHECK(_kadm5_xdr_ret_string_xdr(in, &password));
332
333    INSIST(ent.principal);
334
335    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal);
336    if (ret)
337	goto fail;
338
339    ret = kadm5_create_principal_2(context, &ent, mask, n_ks_tuple, ks_tuple, password);
340
341 fail:
342    krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>");
343    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
344    CHECK(krb5_store_uint32(out, ret)); /* code */
345
346    free(password);
347    kadm5_free_principal_ent(lcontext, &ent);
348
349    free(princ);
350    if (ks_tuple)
351	free(ks_tuple);
352    kadm5_free_principal_ent(context, &ent);
353}
354
355static void
356proc_delete_principal(kadm5_server_context *lcontext,
357		      krb5_storage *in,
358		      krb5_storage *out)
359{
360    uint32_t version;
361    krb5_principal principal;
362    krb5_error_code ret;
363    char *princ = NULL;
364
365    CHECK(krb5_ret_uint32(in, &version));
366    EXPECT_EGT(version, VERSION2);
367    CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
368    CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
369
370    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_DELETE, principal);
371    if (ret)
372	goto fail;
373
374    ret = kadm5_delete_principal(lcontext, principal);
375
376 fail:
377    krb5_warn(lcontext->context, ret, "delete principal: %s", princ ? princ : "<noprinc>");
378    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
379    CHECK(krb5_store_uint32(out, ret)); /* code */
380
381    free(princ);
382    krb5_free_principal(lcontext->context, principal);
383}
384
385static void
386proc_modify_principal(kadm5_server_context *lcontext,
387		      krb5_storage *in,
388		      krb5_storage *out)
389{
390    uint32_t version, mask;
391    kadm5_principal_ent_rec ent;
392    krb5_error_code ret;
393
394    CHECK(krb5_ret_uint32(in, &version));
395    EXPECT_EGT(version, VERSION2);
396    CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
397    INSIST(ent.principal != NULL);
398    CHECK(krb5_ret_uint32(in, &mask));
399
400    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_MODIFY,
401				      ent.principal);
402    if (ret)
403	goto fail;
404
405    ret = kadm5_modify_principal(lcontext, &ent, mask);
406
407 fail:
408    krb5_warn(lcontext->context, ret, "modify principal");
409    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
410    CHECK(krb5_store_uint32(out, ret)); /* code */
411
412    kadm5_free_principal_ent(lcontext, &ent);
413}
414
415static void
416proc_get_principal(kadm5_server_context *lcontext,
417		   krb5_storage *in,
418		   krb5_storage *out)
419{
420    uint32_t version, mask;
421    krb5_principal principal;
422    kadm5_principal_ent_rec ent;
423    krb5_error_code ret;
424    char *princ = NULL;
425
426    memset(&ent, 0, sizeof(ent));
427
428    CHECK(krb5_ret_uint32(in, &version));
429    EXPECT_EGT(version, VERSION2);
430    CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
431    CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
432    CHECK(krb5_ret_uint32(in, &mask));
433
434    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_GET, principal);
435    if(ret)
436	goto fail;
437
438    mask |= KADM5_KVNO | KADM5_PRINCIPAL;
439
440    ret = kadm5_get_principal(lcontext, principal, &ent, mask);
441
442 fail:
443    krb5_warn(lcontext->context, ret, "get principal: %s kvno %d",
444	      princ ? princ : "<unknown>", (int)ent.kvno);
445
446    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
447    CHECK(krb5_store_uint32(out, ret)); /* code */
448    if (ret == 0) {
449	CHECK(_kadm5_xdr_store_principal_ent(lcontext->context, out, &ent));
450    }
451    krb5_free_principal(lcontext->context, principal);
452    kadm5_free_principal_ent(lcontext, &ent);
453}
454
455static void
456proc_chrand_principal_v2(kadm5_server_context *lcontext,
457			 krb5_storage *in,
458			 krb5_storage *out)
459{
460    krb5_error_code ret;
461    krb5_principal principal;
462    uint32_t version;
463    krb5_keyblock *new_keys;
464    int n_keys;
465    char *princ = NULL;
466
467    CHECK(krb5_ret_uint32(in, &version));
468    EXPECT_EGT(version, VERSION2);
469    CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
470    CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
471
472    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal);
473    if(ret)
474	goto fail;
475
476    ret = kadm5_randkey_principal(lcontext, principal,
477				  &new_keys, &n_keys);
478
479 fail:
480    krb5_warn(lcontext->context, ret, "rand key principal v2: %s",
481	      princ ? princ : "<unknown>");
482
483    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
484    CHECK(krb5_store_uint32(out, ret));
485    if (ret == 0) {
486	size_t i;
487	CHECK(krb5_store_int32(out, n_keys));
488
489	for(i = 0; i < n_keys; i++){
490	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
491	    CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue));
492	    krb5_free_keyblock_contents(lcontext->context, &new_keys[i]);
493	}
494	free(new_keys);
495    }
496    krb5_free_principal(lcontext->context, principal);
497    if (princ)
498	free(princ);
499}
500
501static void
502proc_chrand_principal_v3(kadm5_server_context *lcontext,
503			 krb5_storage *in,
504			 krb5_storage *out)
505{
506    krb5_error_code ret;
507    krb5_principal principal;
508    uint32_t version, keepold, n_ks_tuple;
509    krb5_keyblock *new_keys;
510    krb5_key_salt_tuple *ks_tuple;
511    char *princ = NULL;
512    int n_keys;
513
514    CHECK(krb5_ret_uint32(in, &version));
515    EXPECT_EGT(version, VERSION2);
516    CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
517    CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
518    CHECK(krb5_ret_uint32(in, &keepold));
519    ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple);
520
521    ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal);
522    if(ret)
523	goto fail;
524
525    ret = kadm5_randkey_principal_3(context, principal, keepold, n_ks_tuple, ks_tuple,
526				    &new_keys, &n_keys);
527
528 fail:
529    krb5_warn(lcontext->context, ret, "rand key principal v3: %s",
530	      princ ? princ : "<unknown>");
531
532    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
533    CHECK(krb5_store_uint32(out, ret));
534    if (ret == 0) {
535	size_t i;
536	CHECK(krb5_store_int32(out, n_keys));
537
538	for(i = 0; i < n_keys; i++){
539	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
540	    CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue));
541	    krb5_free_keyblock_contents(lcontext->context, &new_keys[i]);
542	}
543	free(new_keys);
544    }
545    if (ks_tuple)
546	free(ks_tuple);
547    krb5_free_principal(lcontext->context, principal);
548    if (princ)
549	free(princ);
550}
551
552
553static void
554proc_init(kadm5_server_context *lcontext,
555	  krb5_storage *in,
556	  krb5_storage *out)
557{
558    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
559    CHECK(krb5_store_uint32(out, 0)); /* code */
560    CHECK(krb5_store_uint32(out, 0)); /* code */
561}
562
563static void
564proc_get_policy(kadm5_server_context *lcontext,
565		krb5_storage *in,
566		krb5_storage *out)
567{
568    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
569    CHECK(krb5_store_uint32(out, KADM5_AUTH_GET)); /* code */
570}
571
572
573struct proc {
574    char *name;
575    void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
576} procs[] = {
577    { "NULL", NULL },
578    { "create principal", proc_create_principal },
579    { "delete principal", proc_delete_principal },
580    { "modify principal", proc_modify_principal },
581    { "rename principal", NULL },
582    { "get principal", proc_get_principal },
583    { "chpass principal", NULL },
584    { "chrand principal v2", proc_chrand_principal_v2 },
585    { "create policy", NULL },
586    { "delete policy", NULL },
587    { "modify policy", NULL },
588    { "get policy", proc_get_policy },
589    { "get privs", NULL },
590    { "init", proc_init },
591    { "get principals", NULL },
592    { "get polices", NULL },
593    { "setkey principal", NULL },
594    { "setkey principal v4", NULL },
595    { "create principal v3", proc_create_principal3 },
596    { "chpass principal v3", NULL },
597    { "chrand principal v3", proc_chrand_principal_v3 },
598    { "setkey principal v3", NULL }
599};
600
601static krb5_error_code
602copyheader(krb5_storage *sp, krb5_data *data)
603{
604    off_t off;
605    ssize_t sret;
606
607    off = krb5_storage_seek(sp, 0, SEEK_CUR);
608
609    CHECK(krb5_data_alloc(data, off));
610    INSIST(off == data->length);
611    krb5_storage_seek(sp, 0, SEEK_SET);
612    sret = krb5_storage_read(sp, data->data, data->length);
613    INSIST(sret == off);
614    INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
615
616    return 0;
617}
618
619struct gctx {
620    krb5_data handle;
621    gss_ctx_id_t ctx;
622    uint32_t seq_num;
623    uint32_t protocol;
624    int done;
625    int inprogress;
626    void *server_handle;
627    void (*verify_header)(struct gctx *, struct _kadm5_xdr_call_header *, krb5_data *);
628    void (*handle_protocol)(struct gctx *, struct _kadm5_xdr_call_header *,
629			    krb5_storage *, krb5_storage *);
630    void (*reply)(struct gctx *, krb5_storage *, krb5_storage *);
631
632};
633
634static void
635setup_context(struct gctx *gctx, gss_name_t src_name)
636{
637    kadm5_config_params realm_params;
638    gss_buffer_desc buf;
639    OM_uint32 maj_stat, min_stat, junk;
640    krb5_error_code ret;
641    char *client;
642
643    INSIST(gctx->done);
644
645    memset(&realm_params, 0, sizeof(realm_params));
646
647    maj_stat = gss_export_name(&min_stat, src_name, &buf);
648    EXPECT(maj_stat, GSS_S_COMPLETE);
649    EXPECT(min_stat, 0);
650
651    CHECK(parse_name(buf.value, buf.length,
652		     GSS_KRB5_MECHANISM, &client));
653
654    gss_release_buffer(&junk, &buf);
655
656    krb5_warnx(context, "%s connected", client);
657
658    ret = kadm5_s_init_with_password_ctx(context,
659					 client,
660					 NULL,
661					 KADM5_ADMIN_SERVICE,
662					 &realm_params,
663					 0, 0,
664					 &gctx->server_handle);
665    EXPECT(ret, 0);
666}
667
668/*
669 * GSS flavor
670 */
671
672static void
673xpcgss_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header)
674{
675    OM_uint32 maj_stat, min_stat;
676    gss_buffer_desc gin, gout;
677
678    EXPECT(chdr->verf.flavor, gctx->protocol);
679
680    /* from first byte to last of credential */
681    gin.value = header->data;
682    gin.length = header->length;
683    gout.value = chdr->verf.data.data;
684    gout.length = chdr->verf.data.length;
685
686    maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL);
687    EXPECT(maj_stat, GSS_S_COMPLETE);
688}
689
690static void
691rpcgss_handle_protocol(struct gctx *gctx,
692		       struct _kadm5_xdr_call_header *chdr,
693		       krb5_storage *msg,
694		       krb5_storage *dreply)
695{
696    OM_uint32 maj_stat, min_stat, junk;
697    gss_buffer_desc gin, gout;
698    struct _kadm5_xdr_gcred gcred;
699
700    memset(&gcred, 0, sizeof(gcred));
701
702    CHECK(_kadm5_xdr_ret_gcred(&chdr->cred.data, &gcred));
703    EXPECT(gcred.version, FLAVOR_GSS_VERSION);
704
705    switch(gcred.proc) {
706    case RPG_DATA: {
707	krb5_data data;
708	int conf_state;
709	uint32_t seq;
710	krb5_storage *sp;
711
712	EXPECT(gcred.service, rpg_privacy);
713
714	INSIST(gctx->done);
715
716	INSIST(krb5_data_cmp(&gcred.handle, &gctx->handle) == 0);
717
718	CHECK(_kadm5_xdr_ret_data_xdr(msg, &data));
719
720	gin.value = data.data;
721	gin.length = data.length;
722
723	maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout,
724			      &conf_state, NULL);
725	krb5_data_free(&data);
726	INSIST(maj_stat == GSS_S_COMPLETE);
727	INSIST(conf_state != 0);
728
729	sp = krb5_storage_from_mem(gout.value, gout.length);
730	INSIST(sp != NULL);
731
732	CHECK(krb5_ret_uint32(sp, &seq));
733	EXPECT(seq, gcred.seq_num);
734
735	/*
736	 * Check sequence number
737	 */
738	INSIST(seq > gctx->seq_num);
739	gctx->seq_num = seq;
740
741	/*
742	 * If context is setup, priv data have the seq_num stored
743	 * first in the block, so add it here before users data is
744	 * added.
745	 */
746	CHECK(krb5_store_uint32(dreply, gctx->seq_num));
747
748	if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) {
749	    krb5_warnx(context, "proc number out of array");
750	} else if (procs[chdr->proc].func == NULL) {
751	    krb5_warnx(context, "proc '%s' never implemented",
752		       procs[chdr->proc].name);
753	} else {
754	    krb5_warnx(context, "proc %s", procs[chdr->proc].name);
755	    INSIST(gctx->server_handle != NULL);
756	    (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply);
757	}
758	krb5_storage_free(sp);
759	gss_release_buffer(&min_stat, &gout);
760
761	break;
762    }
763    case RPG_INIT:
764	INSIST(gctx->inprogress == 0);
765	INSIST(gctx->ctx == NULL);
766
767	gctx->inprogress = 1;
768	/* FALL THOUGH */
769    case RPG_CONTINUE_INIT: {
770	gss_name_t src_name = GSS_C_NO_NAME;
771	krb5_data in;
772
773	INSIST(gctx->inprogress);
774
775	CHECK(_kadm5_xdr_ret_data_xdr(msg, &in));
776
777	gin.value = in.data;
778	gin.length = in.length;
779	gout.value = NULL;
780	gout.length = 0;
781
782	maj_stat = gss_accept_sec_context(&min_stat,
783					  &gctx->ctx,
784					  GSS_C_NO_CREDENTIAL,
785					  &gin,
786					  GSS_C_NO_CHANNEL_BINDINGS,
787					  &src_name,
788					  NULL,
789					  &gout,
790					  NULL,
791					  NULL,
792					  NULL);
793	if (GSS_ERROR(maj_stat)) {
794	    gss_print_errors(context, maj_stat, min_stat);
795	    krb5_errx(context, 1, "gss error, exit");
796	}
797	if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
798
799	    gctx->done = 1;
800	    gctx->verify_header = xpcgss_verify_header;
801
802	    setup_context(gctx, src_name);
803	}
804
805	INSIST(gctx->ctx != GSS_C_NO_CONTEXT);
806
807	CHECK(_kadm5_xdr_store_gss_init_res(dreply, gctx->handle,
808					    maj_stat, min_stat, SEQ_WINDOW_SIZE, &gout));
809	if (gout.value)
810	    gss_release_buffer(&junk, &gout);
811	if (src_name)
812	    gss_release_name(&junk, &src_name);
813
814	break;
815    }
816    case RPG_DESTROY:
817	krb5_errx(context, 1, "client destroyed gss context");
818    default:
819	krb5_errx(context, 1, "client sent unknown gsscode %d",
820		  (int)gcred.proc);
821    }
822
823    krb5_data_free(&gcred.handle);
824}
825
826static void
827rpcgss_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply)
828{
829    uint32_t seqnum = htonl(gctx->seq_num);
830    gss_buffer_desc gin, gout;
831    OM_uint32 maj_stat, min_stat, junk;
832    krb5_data data;
833
834    /*
835     * The first checksum is really the checksum of the
836     * seq_window in the rpc_gss_init_res packet, lets agree
837     * with that.
838     */
839    if (gctx->seq_num == 0)
840	seqnum = htonl(SEQ_WINDOW_SIZE);
841
842    gin.value = &seqnum;
843    gin.length = sizeof(seqnum);
844
845    maj_stat = gss_get_mic(&min_stat, gctx->ctx, 0, &gin, &gout);
846    INSIST(maj_stat == GSS_S_COMPLETE);
847
848    data.data = gout.value;
849    data.length = gout.length;
850
851    CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
852    CHECK(_kadm5_xdr_store_data_xdr(reply, data));
853    gss_release_buffer(&junk, &gout);
854
855    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
856
857    CHECK(krb5_storage_to_data(dreply, &data));
858
859    if (gctx->inprogress) {
860	ssize_t sret;
861	gctx->inprogress = 0;
862	sret = krb5_storage_write(reply, data.data, data.length);
863	INSIST(sret == data.length);
864	krb5_data_free(&data);
865    } else {
866	int conf_state;
867
868	gin.value = data.data;
869	gin.length = data.length;
870
871	maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0,
872			    &gin, &conf_state, &gout);
873	INSIST(maj_stat == GSS_S_COMPLETE);
874	INSIST(conf_state != 0);
875	krb5_data_free(&data);
876
877	data.data = gout.value;
878	data.length = gout.length;
879
880	_kadm5_xdr_store_data_xdr(reply, data);
881	gss_release_buffer(&min_stat, &gout);
882    }
883}
884
885
886/*
887 * GSSAPI flavor
888 */
889
890enum {
891    RPGA_INIT = 1,
892    RPGA_CONTINUE_INIT = 2,
893    RPGA_MSG = 3,
894    RPGA_DESTORY = 4
895};
896
897static void
898xpcgssapi_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header)
899{
900#if 0
901    OM_uint32 maj_stat, min_stat;
902    gss_buffer_desc gin, gout;
903
904    EXPECT(chdr->verf.flavor, gctx->protocol);
905
906    /* from first byte to last of credential */
907    gin.value = header->data;
908    gin.length = header->length;
909    gout.value = chdr->verf.data.data;
910    gout.length = chdr->verf.data.length;
911
912    maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL);
913    EXPECT(maj_stat, GSS_S_COMPLETE);
914#endif
915}
916
917static void
918rpcgssapi_handle_protocol(struct gctx *gctx,
919			  struct _kadm5_xdr_call_header *chdr,
920			  krb5_storage *msg,
921			  krb5_storage *dreply)
922{
923    struct _kadm5_xdr_gacred gacred;
924    OM_uint32 maj_stat, min_stat, junk;
925    gss_buffer_desc gin, gout, gseq;
926
927    CHECK(_kadm5_xdr_ret_gacred(&chdr->cred.data, &gacred));
928    gseq.length = 0;
929    gseq.value = NULL;
930
931    if (gctx->done == 0 && chdr->proc == RPGA_INIT) {
932	INSIST(gacred.handle.length == 0);
933	INSIST(gctx->handle.length == 0);
934
935	CHECK(krb5_data_alloc(&gctx->handle, 16));
936	CCRandomCopyBytes(kCCRandomDefault, gctx->handle.data, gctx->handle.length);
937
938    } else {
939	INSIST(gacred.handle.length != 0);
940	INSIST(krb5_data_cmp(&gacred.handle, &gctx->handle) == 0);
941    }
942
943
944    if (gctx->done == 0) {
945	uint32_t version;
946	krb5_data token, out;
947
948	CHECK(krb5_ret_uint32(msg, &version));
949	CHECK(_kadm5_xdr_ret_data_xdr(msg, &token));
950
951	switch (chdr->proc) {
952	case RPGA_INIT:
953	    INSIST(gctx->inprogress == 0);
954	    INSIST(gctx->ctx == NULL);
955
956	    INSIST(version == 3 || version == 4);
957
958	    gctx->inprogress = 1;
959
960	    /* FALL THOUGH */
961	case RPGA_CONTINUE_INIT: {
962	    gss_name_t src_name = GSS_C_NO_NAME;
963
964	    INSIST(gctx->inprogress);
965
966	    gin.value = token.data;
967	    gin.length = token.length;
968	    gout.value = NULL;
969	    gout.length = 0;
970
971	    maj_stat = gss_accept_sec_context(&min_stat,
972					      &gctx->ctx,
973					      GSS_C_NO_CREDENTIAL,
974					      &gin,
975					      GSS_C_NO_CHANNEL_BINDINGS,
976					      &src_name,
977					      NULL,
978					      &gout,
979					      NULL,
980					      NULL,
981					      NULL);
982	    if (GSS_ERROR(maj_stat)) {
983		gss_print_errors(context, maj_stat, min_stat);
984		krb5_errx(context, 1, "gss error, exit");
985	    }
986	    if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
987		uint32_t netseqnum;
988
989		gctx->done = 1;
990		gctx->verify_header = xpcgssapi_verify_header;
991
992		setup_context(gctx, src_name);
993
994		CCRandomCopyBytes(kCCRandomDefault,
995				  &gctx->seq_num, sizeof(gctx->seq_num));
996
997		netseqnum = htonl(gctx->seq_num);
998		gin.value = &netseqnum;
999		gin.length = sizeof(netseqnum);
1000
1001		CHECK(gss_wrap(&junk, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gseq));
1002	    }
1003
1004	    INSIST(gctx->ctx != GSS_C_NO_CONTEXT);
1005
1006	    /* reply argument */
1007
1008	    CHECK(krb5_store_uint32(dreply, version));
1009	    CHECK(_kadm5_xdr_store_data_xdr(dreply, gctx->handle));
1010	    CHECK(krb5_store_uint32(dreply, maj_stat));
1011	    CHECK(krb5_store_uint32(dreply, min_stat));
1012
1013	    out.data = gout.value;
1014	    out.length = gout.length;
1015	    CHECK(_kadm5_xdr_store_data_xdr(dreply, out));
1016
1017	    out.data = gseq.value;
1018	    out.length = gseq.length;
1019	    CHECK(_kadm5_xdr_store_data_xdr(dreply, out));
1020
1021	    if (gout.value)
1022		gss_release_buffer(&junk, &gout);
1023	    if (gseq.value)
1024		gss_release_buffer(&junk, &gseq);
1025	    if (src_name)
1026		gss_release_name(&junk, &src_name);
1027
1028	    break;
1029	}
1030	default:
1031	    krb5_errx(context, 1, "unsupported init message %d", (int)chdr->proc);
1032	}
1033	krb5_data_free(&token);
1034
1035    } else if (gacred.auth_msg) {
1036	if (chdr->proc == RPGA_MSG)
1037	    krb5_warnx(context, "auth message MSG not supported");
1038	else if (chdr->proc == RPGA_DESTORY)
1039	    krb5_warnx(context, "auth message DESTROY not supported");
1040	else
1041	    krb5_errx(context, 1, "auth message not supported: %d", (int)chdr->proc);
1042    } else {
1043	krb5_storage *sp;
1044	krb5_data data;
1045	int conf_state = 0;
1046	uint32_t seq;
1047
1048	INSIST(gctx->done);
1049
1050	CHECK(_kadm5_xdr_ret_data_xdr(msg, &data));
1051
1052	gin.value = data.data;
1053	gin.length = data.length;
1054
1055	maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout,
1056			      &conf_state, NULL);
1057	krb5_data_free(&data);
1058	INSIST(maj_stat == GSS_S_COMPLETE);
1059	INSIST(conf_state != 0);
1060
1061	sp = krb5_storage_from_mem(gout.value, gout.length);
1062	INSIST(sp != NULL);
1063
1064	CHECK(krb5_ret_uint32(sp, &seq));
1065
1066	/*
1067	 * Check sequence number
1068	 */
1069	INSIST(seq == gctx->seq_num + 1);
1070	gctx->seq_num = seq + 1;
1071
1072	/*
1073	 * If context is setup, priv data have the seq_num stored
1074	 * first in the block, so add it here before users data is
1075	 * added.
1076	 */
1077	CHECK(krb5_store_uint32(dreply, gctx->seq_num));
1078
1079	if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) {
1080	    krb5_warnx(context, "proc number out of array");
1081	} else if (procs[chdr->proc].func == NULL) {
1082	    krb5_warnx(context, "proc '%s' never implemented",
1083		       procs[chdr->proc].name);
1084	} else {
1085	    krb5_warnx(context, "proc %s", procs[chdr->proc].name);
1086	    INSIST(gctx->server_handle != NULL);
1087	    (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply);
1088	}
1089	krb5_storage_free(sp);
1090	gss_release_buffer(&min_stat, &gout);
1091    }
1092
1093    krb5_data_free(&gacred.handle);
1094}
1095
1096static void
1097rpcgssapi_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply)
1098{
1099    uint32_t seqnum = htonl(gctx->seq_num);
1100    gss_buffer_desc gin, gout;
1101    OM_uint32 maj_stat, min_stat, junk;
1102    krb5_data data;
1103
1104    gin.value = &seqnum;
1105    gin.length = sizeof(seqnum);
1106
1107    maj_stat = gss_wrap(&min_stat, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gout);
1108    INSIST(maj_stat == GSS_S_COMPLETE);
1109
1110    data.data = gout.value;
1111    data.length = gout.length;
1112
1113    CHECK(krb5_store_uint32(reply, FLAVOR_GSS_OLD));
1114    CHECK(_kadm5_xdr_store_data_xdr(reply, data));
1115    gss_release_buffer(&junk, &gout);
1116
1117    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1118
1119    CHECK(krb5_storage_to_data(dreply, &data));
1120
1121    if (gctx->inprogress) {
1122	ssize_t sret;
1123	gctx->inprogress = 0;
1124	sret = krb5_storage_write(reply, data.data, data.length);
1125	INSIST(sret == data.length);
1126	krb5_data_free(&data);
1127    } else {
1128	int conf_state;
1129
1130	gin.value = data.data;
1131	gin.length = data.length;
1132
1133	maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0,
1134			    &gin, &conf_state, &gout);
1135	INSIST(maj_stat == GSS_S_COMPLETE);
1136	INSIST(conf_state != 0);
1137	krb5_data_free(&data);
1138
1139	data.data = gout.value;
1140	data.length = gout.length;
1141
1142	_kadm5_xdr_store_data_xdr(reply, data);
1143	gss_release_buffer(&min_stat, &gout);
1144    }
1145}
1146
1147
1148/*
1149 *
1150 */
1151
1152
1153static int
1154process_stream(krb5_context lcontext,
1155	       unsigned char *buf, size_t ilen,
1156	       krb5_storage *sp)
1157{
1158    krb5_error_code ret;
1159    krb5_storage *msg, *reply, *dreply;
1160    struct gctx gctx;
1161
1162    memset(&gctx, 0, sizeof(gctx));
1163
1164    msg = krb5_storage_emem();
1165    reply = krb5_storage_emem();
1166    dreply = krb5_storage_emem();
1167
1168    /*
1169     * First packet comes partly from the caller
1170     */
1171
1172    INSIST(ilen >= 4);
1173
1174    while (1) {
1175	struct _kadm5_xdr_call_header chdr;
1176	uint32_t mtype;
1177	krb5_data headercopy;
1178
1179	krb5_storage_truncate(dreply, 0);
1180	krb5_storage_truncate(reply, 0);
1181	krb5_storage_truncate(msg, 0);
1182
1183	krb5_data_zero(&headercopy);
1184	memset(&chdr, 0, sizeof(chdr));
1185
1186	/*
1187	 * This is very icky to handle the the auto-detection between
1188	 * the Heimdal protocol and the MIT ONC-RPC based protocol.
1189	 */
1190
1191	if (ilen) {
1192	    int last_fragment;
1193	    unsigned long len;
1194	    ssize_t slen;
1195	    unsigned char tmp[4];
1196
1197	    if (ilen < 4) {
1198		memcpy(tmp, buf, ilen);
1199		slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
1200		INSIST(slen == sizeof(tmp) - ilen);
1201
1202		ilen = sizeof(tmp);
1203		buf = tmp;
1204	    }
1205	    INSIST(ilen >= 4);
1206
1207	    _krb5_get_int(buf, &len, 4);
1208	    last_fragment = (len & LAST_FRAGMENT) != 0;
1209	    len &= ~LAST_FRAGMENT;
1210
1211	    ilen -= 4;
1212	    buf += 4;
1213
1214	    if (ilen) {
1215		if (len < ilen) {
1216		    slen = krb5_storage_write(msg, buf, len);
1217		    INSIST(slen == len);
1218		    ilen -= len;
1219		    len = 0;
1220		} else {
1221		    slen = krb5_storage_write(msg, buf, ilen);
1222		    INSIST(slen == ilen);
1223		    len -= ilen;
1224		}
1225	    }
1226
1227	    CHECK(read_data(sp, msg, len));
1228
1229	    if (!last_fragment) {
1230		ret = collect_fragments(sp, msg);
1231		if (ret == HEIM_ERR_EOF)
1232		    krb5_errx(lcontext, 0, "client disconnected");
1233		INSIST(ret == 0);
1234	    }
1235	} else {
1236
1237	    ret = collect_fragments(sp, msg);
1238	    if (ret == HEIM_ERR_EOF)
1239		krb5_errx(lcontext, 0, "client disconnected");
1240	    INSIST(ret == 0);
1241	}
1242	krb5_storage_seek(msg, 0, SEEK_SET);
1243
1244	CHECK(krb5_ret_uint32(msg, &chdr.xid));
1245	CHECK(krb5_ret_uint32(msg, &mtype));
1246	CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
1247	CHECK(krb5_ret_uint32(msg, &chdr.prog));
1248	CHECK(krb5_ret_uint32(msg, &chdr.vers));
1249	CHECK(krb5_ret_uint32(msg, &chdr.proc));
1250	CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.cred));
1251	CHECK(copyheader(msg, &headercopy));
1252	CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.verf));
1253
1254	EXPECT(chdr.rpcvers, RPC_VERSION);
1255	EXPECT(chdr.prog, KADM_SERVER);
1256	EXPECT(chdr.vers, VVERSION);
1257	if (gctx.protocol == 0) {
1258	    gctx.protocol = chdr.cred.flavor;
1259
1260	    INSIST(gctx.handle_protocol == NULL);
1261
1262	    switch(gctx.protocol) {
1263	    case FLAVOR_GSS:
1264		gctx.handle_protocol = rpcgss_handle_protocol;
1265		gctx.reply = rpcgss_reply;
1266		break;
1267	    case FLAVOR_GSS_OLD:
1268		gctx.handle_protocol = rpcgssapi_handle_protocol;
1269		gctx.reply = rpcgssapi_reply;
1270		break;
1271	    default:
1272		krb5_errx(lcontext, 0, "unsupported protocol version: %d", (int)gctx.protocol);
1273	    }
1274
1275	} else {
1276	    EXPECT(chdr.cred.flavor, gctx.protocol);
1277	}
1278
1279	if (gctx.verify_header)
1280	    gctx.verify_header(&gctx, &chdr, &headercopy);
1281
1282	gctx.handle_protocol(&gctx, &chdr, msg, dreply);
1283
1284	krb5_data_free(&chdr.cred.data);
1285	krb5_data_free(&chdr.verf.data);
1286	krb5_data_free(&headercopy);
1287
1288	CHECK(krb5_store_uint32(reply, chdr.xid));
1289	CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1290	CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1291
1292	if (!gctx.done) {
1293	    krb5_data data;
1294
1295	    CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1296	    CHECK(krb5_store_uint32(reply, 0)); /* length */
1297
1298	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1299
1300	    CHECK(krb5_storage_to_data(dreply, &data));
1301	    INSIST(krb5_storage_write(reply, data.data, data.length) == data.length);
1302	    krb5_data_free(&data);
1303	} else {
1304	    INSIST(gctx.reply != NULL);
1305
1306	    gctx.reply(&gctx, dreply, reply);
1307	}
1308
1309	{
1310	    krb5_data data;
1311	    ssize_t sret;
1312	    CHECK(krb5_storage_to_data(reply, &data));
1313	    CHECK(krb5_store_uint32(sp, ((uint32_t)data.length) | LAST_FRAGMENT));
1314	    sret = krb5_storage_write(sp, data.data, data.length);
1315	    INSIST(sret == data.length);
1316	    krb5_data_free(&data);
1317	}
1318
1319    }
1320}
1321
1322int
1323handle_mit(krb5_context lcontext, void *buf, size_t len, krb5_socket_t sock)
1324{
1325    krb5_storage *sp;
1326
1327    dcontext = context;
1328
1329    sp = krb5_storage_from_fd(sock);
1330    INSIST(sp != NULL);
1331
1332    process_stream(lcontext, buf, len, sp);
1333
1334    return 0;
1335}
1336