rpc.c revision 302408
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
40static 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
53static 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
67struct opaque_auth {
68    uint32_t flavor;
69    krb5_data data;
70};
71
72struct 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
82enum {
83    RPG_DATA = 0,
84    RPG_INIT = 1,
85    RPG_CONTINUE_INIT = 2,
86    RPG_DESTROY = 3
87};
88
89enum {
90    rpg_privacy = 3
91};
92
93/*
94struct 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
103struct gcred {
104    uint32_t version;
105    uint32_t proc;
106    uint32_t seq_num;
107    uint32_t service;
108    krb5_data handle;
109};
110
111static int
112parse_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
167static void
168gss_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
190static void
191gss_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
198static int
199read_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
221static int
222collect_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
245static krb5_error_code
246store_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
265static krb5_error_code
266ret_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
287static krb5_error_code
288ret_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
297static int
298ret_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
318static krb5_error_code
319store_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
339static int
340store_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
352static int
353ret_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
368static int
369store_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
380static int
381ret_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
395static int
396store_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
449static int
450ret_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
544static void
545proc_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
580static void
581proc_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
607static void
608proc_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
642static void
643proc_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
683static void
684proc_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
693struct 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
721static krb5_error_code
722copyheader(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
739struct gctx {
740    krb5_data handle;
741    gss_ctx_id_t ctx;
742    uint32_t seq_num;
743    int done;
744    int inprogress;
745};
746
747static int
748process_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
1097int
1098handle_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