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