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