1226031Sstas/*
2226031Sstas * Copyright (c) 2008 Kungliga Tekniska H��gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Redistribution and use in source and binary forms, with or without
7226031Sstas * modification, are permitted provided that the following conditions
8226031Sstas * are met:
9226031Sstas *
10226031Sstas * 1. Redistributions of source code must retain the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer.
12226031Sstas *
13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
14226031Sstas *    notice, this list of conditions and the following disclaimer in the
15226031Sstas *    documentation and/or other materials provided with the distribution.
16226031Sstas *
17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
18226031Sstas *    may be used to endorse or promote products derived from this software
19226031Sstas *    without specific prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include "kadmin_locl.h"
35226031Sstas
36233294Sstas#include <gssapi/gssapi.h>
37233294Sstas//#include <gssapi_krb5.h>
38233294Sstas//#include <gssapi_spnego.h>
39226031Sstas
40233294Sstasstatic gss_OID_desc krb5_mechanism =
41233294Sstas{9, (void *)(uintptr_t) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
42233294Sstas#define GSS_KRB5_MECHANISM (&krb5_mechanism)
43233294Sstas
44226031Sstas#define CHECK(x)							\
45226031Sstas	do {								\
46226031Sstas		int __r;						\
47226031Sstas		if ((__r = (x))) {					\
48226031Sstas			krb5_errx(dcontext, 1, "Failed (%d) on %s:%d",	\
49226031Sstas			    __r, __FILE__, __LINE__);			\
50226031Sstas		}							\
51226031Sstas	} while(0)
52226031Sstas
53226031Sstasstatic krb5_context dcontext;
54226031Sstas
55226031Sstas#define INSIST(x) CHECK(!(x))
56226031Sstas
57226031Sstas#define VERSION2 0x12345702
58226031Sstas
59226031Sstas#define LAST_FRAGMENT 0x80000000
60226031Sstas
61226031Sstas#define RPC_VERSION 2
62226031Sstas#define KADM_SERVER 2112
63226031Sstas#define VVERSION 2
64226031Sstas#define FLAVOR_GSS 6
65226031Sstas#define FLAVOR_GSS_VERSION 1
66226031Sstas
67226031Sstasstruct opaque_auth {
68226031Sstas    uint32_t flavor;
69226031Sstas    krb5_data data;
70226031Sstas};
71226031Sstas
72226031Sstasstruct call_header {
73226031Sstas    uint32_t xid;
74226031Sstas    uint32_t rpcvers;
75226031Sstas    uint32_t prog;
76226031Sstas    uint32_t vers;
77226031Sstas    uint32_t proc;
78226031Sstas    struct opaque_auth cred;
79226031Sstas    struct opaque_auth verf;
80226031Sstas};
81226031Sstas
82226031Sstasenum {
83226031Sstas    RPG_DATA = 0,
84226031Sstas    RPG_INIT = 1,
85226031Sstas    RPG_CONTINUE_INIT = 2,
86226031Sstas    RPG_DESTROY = 3
87226031Sstas};
88226031Sstas
89226031Sstasenum {
90226031Sstas    rpg_privacy = 3
91226031Sstas};
92226031Sstas
93226031Sstas/*
94226031Sstasstruct chrand_ret {
95226031Sstas	krb5_ui_4 api_version;
96226031Sstas	kadm5_ret_t ret;
97226031Sstas	int n_keys;
98226031Sstas	krb5_keyblock *keys;
99226031Sstas};
100226031Sstas*/
101226031Sstas
102226031Sstas
103226031Sstasstruct gcred {
104226031Sstas    uint32_t version;
105226031Sstas    uint32_t proc;
106226031Sstas    uint32_t seq_num;
107226031Sstas    uint32_t service;
108226031Sstas    krb5_data handle;
109226031Sstas};
110226031Sstas
111226031Sstasstatic int
112226031Sstasparse_name(const unsigned char *p, size_t len,
113226031Sstas	   const gss_OID oid, char **name)
114226031Sstas{
115226031Sstas    size_t l;
116226031Sstas
117226031Sstas    if (len < 4)
118226031Sstas	return 1;
119226031Sstas
120226031Sstas    /* TOK_ID */
121226031Sstas    if (memcmp(p, "\x04\x01", 2) != 0)
122226031Sstas	return 1;
123226031Sstas    len -= 2;
124226031Sstas    p += 2;
125226031Sstas
126226031Sstas    /* MECH_LEN */
127226031Sstas    l = (p[0] << 8) | p[1];
128226031Sstas    len -= 2;
129226031Sstas    p += 2;
130226031Sstas    if (l < 2 || len < l)
131226031Sstas	return 1;
132226031Sstas
133226031Sstas    /* oid wrapping */
134226031Sstas    if (p[0] != 6 || p[1] != l - 2)
135226031Sstas	return 1;
136226031Sstas    p += 2;
137226031Sstas    l -= 2;
138226031Sstas    len -= 2;
139226031Sstas
140226031Sstas    /* MECH */
141226031Sstas    if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
142226031Sstas	return 1;
143226031Sstas    len -= l;
144226031Sstas    p += l;
145226031Sstas
146226031Sstas    /* MECHNAME_LEN */
147226031Sstas    if (len < 4)
148226031Sstas	return 1;
149226031Sstas    l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
150226031Sstas    len -= 4;
151226031Sstas    p += 4;
152226031Sstas
153226031Sstas    /* MECH NAME */
154226031Sstas    if (len != l)
155226031Sstas	return 1;
156226031Sstas
157226031Sstas    *name = malloc(l + 1);
158226031Sstas    INSIST(*name != NULL);
159226031Sstas    memcpy(*name, p, l);
160226031Sstas    (*name)[l] = '\0';
161226031Sstas
162226031Sstas    return 0;
163226031Sstas}
164226031Sstas
165226031Sstas
166226031Sstas
167226031Sstasstatic void
168226031Sstasgss_error(krb5_context contextp,
169226031Sstas	  gss_OID mech, OM_uint32 type, OM_uint32 error)
170226031Sstas{
171226031Sstas    OM_uint32 new_stat;
172226031Sstas    OM_uint32 msg_ctx = 0;
173226031Sstas    gss_buffer_desc status_string;
174226031Sstas    OM_uint32 ret;
175226031Sstas
176226031Sstas    do {
177226031Sstas	ret = gss_display_status (&new_stat,
178226031Sstas				  error,
179226031Sstas				  type,
180226031Sstas				  mech,
181226031Sstas				  &msg_ctx,
182226031Sstas				  &status_string);
183226031Sstas	krb5_warnx(contextp, "%.*s",
184226031Sstas		   (int)status_string.length,
185226031Sstas		   (char *)status_string.value);
186226031Sstas	gss_release_buffer (&new_stat, &status_string);
187226031Sstas    } while (!GSS_ERROR(ret) && msg_ctx != 0);
188226031Sstas}
189226031Sstas
190226031Sstasstatic void
191226031Sstasgss_print_errors (krb5_context contextp,
192226031Sstas		  OM_uint32 maj_stat, OM_uint32 min_stat)
193226031Sstas{
194226031Sstas    gss_error(contextp, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
195226031Sstas    gss_error(contextp, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
196226031Sstas}
197226031Sstas
198226031Sstasstatic int
199226031Sstasread_data(krb5_storage *sp, krb5_storage *msg, size_t len)
200226031Sstas{
201226031Sstas    char buf[1024];
202226031Sstas
203226031Sstas    while (len) {
204226031Sstas	size_t tlen = len;
205226031Sstas	ssize_t slen;
206226031Sstas
207226031Sstas	if (tlen > sizeof(buf))
208226031Sstas	    tlen = sizeof(buf);
209226031Sstas
210226031Sstas	slen = krb5_storage_read(sp, buf, tlen);
211226031Sstas	INSIST((size_t)slen == tlen);
212226031Sstas
213226031Sstas	slen = krb5_storage_write(msg, buf, tlen);
214226031Sstas	INSIST((size_t)slen == tlen);
215226031Sstas
216226031Sstas	len -= tlen;
217226031Sstas    }
218226031Sstas    return 0;
219226031Sstas}
220226031Sstas
221226031Sstasstatic int
222226031Sstascollect_framents(krb5_storage *sp, krb5_storage *msg)
223226031Sstas{
224226031Sstas    krb5_error_code ret;
225226031Sstas    uint32_t len;
226226031Sstas    int last_fragment;
227226031Sstas    size_t total_len = 0;
228226031Sstas
229226031Sstas    do {
230226031Sstas	ret = krb5_ret_uint32(sp, &len);
231226031Sstas	if (ret)
232226031Sstas	    return ret;
233226031Sstas
234226031Sstas	last_fragment = (len & LAST_FRAGMENT);
235226031Sstas	len &= ~LAST_FRAGMENT;
236226031Sstas
237226031Sstas	CHECK(read_data(sp, msg, len));
238226031Sstas	total_len += len;
239226031Sstas
240226031Sstas    } while(!last_fragment || total_len == 0);
241226031Sstas
242226031Sstas    return 0;
243226031Sstas}
244226031Sstas
245226031Sstasstatic krb5_error_code
246226031Sstasstore_data_xdr(krb5_storage *sp, krb5_data data)
247226031Sstas{
248226031Sstas    krb5_error_code ret;
249226031Sstas    size_t res;
250226031Sstas
251226031Sstas    ret = krb5_store_data(sp, data);
252226031Sstas    if (ret)
253226031Sstas	return ret;
254226031Sstas    res = 4 - (data.length % 4);
255226031Sstas    if (res != 4) {
256226031Sstas	static const char zero[4] = { 0, 0, 0, 0 };
257226031Sstas
258226031Sstas	ret = krb5_storage_write(sp, zero, res);
259226031Sstas	if((size_t)ret != res)
260226031Sstas	    return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
261226031Sstas    }
262226031Sstas    return 0;
263226031Sstas}
264226031Sstas
265226031Sstasstatic krb5_error_code
266226031Sstasret_data_xdr(krb5_storage *sp, krb5_data *data)
267226031Sstas{
268226031Sstas    krb5_error_code ret;
269226031Sstas    ret = krb5_ret_data(sp, data);
270226031Sstas    if (ret)
271226031Sstas	return ret;
272226031Sstas
273226031Sstas    if ((data->length % 4) != 0) {
274226031Sstas	char buf[4];
275226031Sstas	size_t res;
276226031Sstas
277226031Sstas	res = 4 - (data->length % 4);
278226031Sstas	if (res != 4) {
279226031Sstas	    ret = krb5_storage_read(sp, buf, res);
280226031Sstas	    if((size_t)ret != res)
281226031Sstas		return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
282226031Sstas	}
283226031Sstas    }
284226031Sstas    return 0;
285226031Sstas}
286226031Sstas
287226031Sstasstatic krb5_error_code
288226031Sstasret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
289226031Sstas{
290226031Sstas    krb5_error_code ret;
291226031Sstas    ret = krb5_ret_uint32(msg, &ao->flavor);
292226031Sstas    if (ret) return ret;
293226031Sstas    ret = ret_data_xdr(msg, &ao->data);
294226031Sstas    return ret;
295226031Sstas}
296226031Sstas
297226031Sstasstatic int
298226031Sstasret_gcred(krb5_data *data, struct gcred *gcred)
299226031Sstas{
300226031Sstas    krb5_storage *sp;
301226031Sstas
302226031Sstas    memset(gcred, 0, sizeof(*gcred));
303226031Sstas
304226031Sstas    sp = krb5_storage_from_data(data);
305226031Sstas    INSIST(sp != NULL);
306226031Sstas
307226031Sstas    CHECK(krb5_ret_uint32(sp, &gcred->version));
308226031Sstas    CHECK(krb5_ret_uint32(sp, &gcred->proc));
309226031Sstas    CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
310226031Sstas    CHECK(krb5_ret_uint32(sp, &gcred->service));
311226031Sstas    CHECK(ret_data_xdr(sp, &gcred->handle));
312226031Sstas
313226031Sstas    krb5_storage_free(sp);
314226031Sstas
315226031Sstas    return 0;
316226031Sstas}
317226031Sstas
318226031Sstasstatic krb5_error_code
319226031Sstasstore_gss_init_res(krb5_storage *sp, krb5_data handle,
320226031Sstas		   OM_uint32 maj_stat, OM_uint32 min_stat,
321226031Sstas		   uint32_t seq_window, gss_buffer_t gout)
322226031Sstas{
323226031Sstas    krb5_error_code ret;
324226031Sstas    krb5_data out;
325226031Sstas
326226031Sstas    out.data = gout->value;
327226031Sstas    out.length = gout->length;
328226031Sstas
329226031Sstas    ret = store_data_xdr(sp, handle);
330226031Sstas    if (ret) return ret;
331226031Sstas    ret = krb5_store_uint32(sp, maj_stat);
332226031Sstas    if (ret) return ret;
333226031Sstas    ret = krb5_store_uint32(sp, min_stat);
334226031Sstas    if (ret) return ret;
335226031Sstas    ret = store_data_xdr(sp, out);
336226031Sstas    return ret;
337226031Sstas}
338226031Sstas
339226031Sstasstatic int
340226031Sstasstore_string_xdr(krb5_storage *sp, const char *str)
341226031Sstas{
342226031Sstas    krb5_data c;
343226031Sstas    if (str) {
344226031Sstas	c.data = rk_UNCONST(str);
345226031Sstas	c.length = strlen(str) + 1;
346226031Sstas    } else
347226031Sstas	krb5_data_zero(&c);
348226031Sstas
349226031Sstas    return store_data_xdr(sp, c);
350226031Sstas}
351226031Sstas
352226031Sstasstatic int
353226031Sstasret_string_xdr(krb5_storage *sp, char **str)
354226031Sstas{
355226031Sstas    krb5_data c;
356226031Sstas    *str = NULL;
357226031Sstas    CHECK(ret_data_xdr(sp, &c));
358226031Sstas    if (c.length) {
359226031Sstas	*str = malloc(c.length + 1);
360226031Sstas	INSIST(*str != NULL);
361226031Sstas	memcpy(*str, c.data, c.length);
362226031Sstas	(*str)[c.length] = '\0';
363226031Sstas    }
364226031Sstas    krb5_data_free(&c);
365226031Sstas    return 0;
366226031Sstas}
367226031Sstas
368226031Sstasstatic int
369226031Sstasstore_principal_xdr(krb5_context contextp,
370226031Sstas		    krb5_storage *sp,
371226031Sstas		    krb5_principal p)
372226031Sstas{
373226031Sstas    char *str;
374226031Sstas    CHECK(krb5_unparse_name(contextp, p, &str));
375226031Sstas    CHECK(store_string_xdr(sp, str));
376226031Sstas    free(str);
377226031Sstas    return 0;
378226031Sstas}
379226031Sstas
380226031Sstasstatic int
381226031Sstasret_principal_xdr(krb5_context contextp,
382226031Sstas		  krb5_storage *sp,
383226031Sstas		  krb5_principal *p)
384226031Sstas{
385226031Sstas    char *str;
386226031Sstas    *p = NULL;
387226031Sstas    CHECK(ret_string_xdr(sp, &str));
388226031Sstas    if (str) {
389226031Sstas	CHECK(krb5_parse_name(contextp, str, p));
390226031Sstas	free(str);
391226031Sstas    }
392226031Sstas    return 0;
393226031Sstas}
394226031Sstas
395226031Sstasstatic int
396226031Sstasstore_principal_ent(krb5_context contextp,
397226031Sstas		    krb5_storage *sp,
398226031Sstas		    kadm5_principal_ent_rec *ent)
399226031Sstas{
400226031Sstas    int i;
401226031Sstas
402226031Sstas    CHECK(store_principal_xdr(contextp, sp, ent->principal));
403226031Sstas    CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
404226031Sstas    CHECK(krb5_store_uint32(sp, ent->pw_expiration));
405226031Sstas    CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
406226031Sstas    CHECK(krb5_store_uint32(sp, ent->max_life));
407226031Sstas    CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
408226031Sstas    if (ent->mod_name)
409226031Sstas	CHECK(store_principal_xdr(contextp, sp, ent->mod_name));
410226031Sstas    CHECK(krb5_store_uint32(sp, ent->mod_date));
411226031Sstas    CHECK(krb5_store_uint32(sp, ent->attributes));
412226031Sstas    CHECK(krb5_store_uint32(sp, ent->kvno));
413226031Sstas    CHECK(krb5_store_uint32(sp, ent->mkvno));
414226031Sstas    CHECK(store_string_xdr(sp, ent->policy));
415226031Sstas    CHECK(krb5_store_int32(sp, ent->aux_attributes));
416226031Sstas    CHECK(krb5_store_int32(sp, ent->max_renewable_life));
417226031Sstas    CHECK(krb5_store_int32(sp, ent->last_success));
418226031Sstas    CHECK(krb5_store_int32(sp, ent->last_failed));
419226031Sstas    CHECK(krb5_store_int32(sp, ent->fail_auth_count));
420226031Sstas    CHECK(krb5_store_int32(sp, ent->n_key_data));
421226031Sstas    CHECK(krb5_store_int32(sp, ent->n_tl_data));
422226031Sstas    CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
423226031Sstas    if (ent->n_tl_data) {
424226031Sstas	krb5_tl_data *tp;
425226031Sstas
426226031Sstas	for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
427226031Sstas	    krb5_data c;
428226031Sstas	    c.length = tp->tl_data_length;
429226031Sstas	    c.data = tp->tl_data_contents;
430226031Sstas
431226031Sstas	    CHECK(krb5_store_int32(sp, 0)); /* last item */
432226031Sstas	    CHECK(krb5_store_int32(sp, tp->tl_data_type));
433226031Sstas	    CHECK(store_data_xdr(sp, c));
434226031Sstas	}
435226031Sstas	CHECK(krb5_store_int32(sp, 1)); /* last item */
436226031Sstas    }
437226031Sstas
438226031Sstas    CHECK(krb5_store_int32(sp, ent->n_key_data));
439226031Sstas    for (i = 0; i < ent->n_key_data; i++) {
440226031Sstas	CHECK(krb5_store_uint32(sp, 2));
441226031Sstas	CHECK(krb5_store_uint32(sp, ent->kvno));
442226031Sstas	CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
443226031Sstas	CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
444226031Sstas    }
445226031Sstas
446226031Sstas    return 0;
447226031Sstas}
448226031Sstas
449226031Sstasstatic int
450226031Sstasret_principal_ent(krb5_context contextp,
451226031Sstas		  krb5_storage *sp,
452226031Sstas		  kadm5_principal_ent_rec *ent)
453226031Sstas{
454226031Sstas    uint32_t flag, num;
455226031Sstas    size_t i;
456226031Sstas
457226031Sstas    memset(ent, 0, sizeof(*ent));
458226031Sstas
459226031Sstas    CHECK(ret_principal_xdr(contextp, sp, &ent->principal));
460226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
461226031Sstas    ent->princ_expire_time = flag;
462226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
463226031Sstas    ent->pw_expiration = flag;
464226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
465226031Sstas    ent->last_pwd_change = flag;
466226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
467226031Sstas    ent->max_life = flag;
468226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
469226031Sstas    if (flag == 0)
470226031Sstas	ret_principal_xdr(contextp, sp, &ent->mod_name);
471226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
472226031Sstas    ent->mod_date = flag;
473226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
474226031Sstas    ent->attributes = flag;
475226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
476226031Sstas    ent->kvno = flag;
477226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
478226031Sstas    ent->mkvno = flag;
479226031Sstas    CHECK(ret_string_xdr(sp, &ent->policy));
480226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
481226031Sstas    ent->aux_attributes = flag;
482226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
483226031Sstas    ent->max_renewable_life = flag;
484226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
485226031Sstas    ent->last_success = flag;
486226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
487226031Sstas    ent->last_failed = flag;
488226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
489226031Sstas    ent->fail_auth_count = flag;
490226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
491226031Sstas    ent->n_key_data = flag;
492226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
493226031Sstas    ent->n_tl_data = flag;
494226031Sstas    CHECK(krb5_ret_uint32(sp, &flag));
495226031Sstas    if (flag == 0) {
496226031Sstas	krb5_tl_data **tp = &ent->tl_data;
497226031Sstas	size_t count = 0;
498226031Sstas
499226031Sstas	while(1) {
500226031Sstas	    krb5_data c;
501226031Sstas	    CHECK(krb5_ret_uint32(sp, &flag)); /* last item */
502226031Sstas	    if (flag)
503226031Sstas		break;
504226031Sstas	    *tp = calloc(1, sizeof(**tp));
505226031Sstas	    INSIST(*tp != NULL);
506226031Sstas	    CHECK(krb5_ret_uint32(sp, &flag));
507226031Sstas	    (*tp)->tl_data_type = flag;
508226031Sstas	    CHECK(ret_data_xdr(sp, &c));
509226031Sstas	    (*tp)->tl_data_length = c.length;
510226031Sstas	    (*tp)->tl_data_contents = c.data;
511226031Sstas	    tp = &(*tp)->tl_data_next;
512226031Sstas
513226031Sstas	    count++;
514226031Sstas	}
515226031Sstas	INSIST((size_t)ent->n_tl_data == count);
516226031Sstas    } else {
517226031Sstas	INSIST(ent->n_tl_data == 0);
518226031Sstas    }
519226031Sstas
520226031Sstas    CHECK(krb5_ret_uint32(sp, &num));
521226031Sstas    INSIST(num == (uint32_t)ent->n_key_data);
522226031Sstas
523226031Sstas    ent->key_data = calloc(num, sizeof(ent->key_data[0]));
524226031Sstas    INSIST(ent->key_data != NULL);
525226031Sstas
526226031Sstas    for (i = 0; i < num; i++) {
527226031Sstas	CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
528226031Sstas	INSIST(flag > 1);
529226031Sstas	CHECK(krb5_ret_uint32(sp, &flag));
530226031Sstas	ent->kvno = flag;
531226031Sstas	CHECK(krb5_ret_uint32(sp, &flag));
532226031Sstas	ent->key_data[i].key_data_type[0] = flag;
533226031Sstas	CHECK(krb5_ret_uint32(sp, &flag));
534226031Sstas	ent->key_data[i].key_data_type[1] = flag;
535226031Sstas    }
536226031Sstas
537226031Sstas    return 0;
538226031Sstas}
539226031Sstas
540226031Sstas/*
541226031Sstas *
542226031Sstas */
543226031Sstas
544226031Sstasstatic void
545226031Sstasproc_create_principal(kadm5_server_context *contextp,
546226031Sstas		      krb5_storage *in,
547226031Sstas		      krb5_storage *out)
548226031Sstas{
549226031Sstas    uint32_t version, mask;
550226031Sstas    kadm5_principal_ent_rec ent;
551226031Sstas    krb5_error_code ret;
552226031Sstas    char *password;
553226031Sstas
554226031Sstas    memset(&ent, 0, sizeof(ent));
555226031Sstas
556226031Sstas    CHECK(krb5_ret_uint32(in, &version));
557226031Sstas    INSIST(version == VERSION2);
558226031Sstas    CHECK(ret_principal_ent(contextp->context, in, &ent));
559226031Sstas    CHECK(krb5_ret_uint32(in, &mask));
560226031Sstas    CHECK(ret_string_xdr(in, &password));
561226031Sstas
562226031Sstas    INSIST(ent.principal);
563226031Sstas
564226031Sstas
565226031Sstas    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal);
566226031Sstas    if (ret)
567226031Sstas	goto fail;
568226031Sstas
569226031Sstas    ret = kadm5_create_principal(contextp, &ent, mask, password);
570226031Sstas
571226031Sstas fail:
572226031Sstas    krb5_warn(contextp->context, ret, "create principal");
573226031Sstas    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
574226031Sstas    CHECK(krb5_store_uint32(out, ret)); /* code */
575226031Sstas
576226031Sstas    free(password);
577226031Sstas    kadm5_free_principal_ent(contextp, &ent);
578226031Sstas}
579226031Sstas
580226031Sstasstatic void
581226031Sstasproc_delete_principal(kadm5_server_context *contextp,
582226031Sstas		      krb5_storage *in,
583226031Sstas		      krb5_storage *out)
584226031Sstas{
585226031Sstas    uint32_t version;
586226031Sstas    krb5_principal princ;
587226031Sstas    krb5_error_code ret;
588226031Sstas
589226031Sstas    CHECK(krb5_ret_uint32(in, &version));
590226031Sstas    INSIST(version == VERSION2);
591226031Sstas    CHECK(ret_principal_xdr(contextp->context, in, &princ));
592226031Sstas
593226031Sstas    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
594226031Sstas    if (ret)
595226031Sstas	goto fail;
596226031Sstas
597226031Sstas    ret = kadm5_delete_principal(contextp, princ);
598226031Sstas
599226031Sstas fail:
600226031Sstas    krb5_warn(contextp->context, ret, "delete principal");
601226031Sstas    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
602226031Sstas    CHECK(krb5_store_uint32(out, ret)); /* code */
603226031Sstas
604226031Sstas    krb5_free_principal(contextp->context, princ);
605226031Sstas}
606226031Sstas
607226031Sstasstatic void
608226031Sstasproc_get_principal(kadm5_server_context *contextp,
609226031Sstas		   krb5_storage *in,
610226031Sstas		   krb5_storage *out)
611226031Sstas{
612226031Sstas    uint32_t version, mask;
613226031Sstas    krb5_principal princ;
614226031Sstas    kadm5_principal_ent_rec ent;
615226031Sstas    krb5_error_code ret;
616226031Sstas
617226031Sstas    memset(&ent, 0, sizeof(ent));
618226031Sstas
619226031Sstas    CHECK(krb5_ret_uint32(in, &version));
620226031Sstas    INSIST(version == VERSION2);
621226031Sstas    CHECK(ret_principal_xdr(contextp->context, in, &princ));
622226031Sstas    CHECK(krb5_ret_uint32(in, &mask));
623226031Sstas
624226031Sstas    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
625226031Sstas    if(ret)
626226031Sstas	goto fail;
627226031Sstas
628226031Sstas    ret = kadm5_get_principal(contextp, princ, &ent, mask);
629226031Sstas
630226031Sstas fail:
631226031Sstas    krb5_warn(contextp->context, ret, "get principal principal");
632226031Sstas
633226031Sstas    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
634226031Sstas    CHECK(krb5_store_uint32(out, ret)); /* code */
635226031Sstas    if (ret == 0) {
636226031Sstas	CHECK(store_principal_ent(contextp->context, out, &ent));
637226031Sstas    }
638226031Sstas    krb5_free_principal(contextp->context, princ);
639226031Sstas    kadm5_free_principal_ent(contextp, &ent);
640226031Sstas}
641226031Sstas
642226031Sstasstatic void
643226031Sstasproc_chrand_principal_v2(kadm5_server_context *contextp,
644226031Sstas			 krb5_storage *in,
645226031Sstas			 krb5_storage *out)
646226031Sstas{
647226031Sstas    krb5_error_code ret;
648226031Sstas    krb5_principal princ;
649226031Sstas    uint32_t version;
650226031Sstas    krb5_keyblock *new_keys;
651226031Sstas    int n_keys;
652226031Sstas
653226031Sstas    CHECK(krb5_ret_uint32(in, &version));
654226031Sstas    INSIST(version == VERSION2);
655226031Sstas    CHECK(ret_principal_xdr(contextp->context, in, &princ));
656226031Sstas
657226031Sstas    ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
658226031Sstas    if(ret)
659226031Sstas	goto fail;
660226031Sstas
661226031Sstas    ret = kadm5_randkey_principal(contextp, princ,
662226031Sstas				  &new_keys, &n_keys);
663226031Sstas
664226031Sstas fail:
665226031Sstas    krb5_warn(contextp->context, ret, "rand key principal");
666226031Sstas
667226031Sstas    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
668226031Sstas    CHECK(krb5_store_uint32(out, ret));
669226031Sstas    if (ret == 0) {
670226031Sstas	int i;
671226031Sstas	CHECK(krb5_store_int32(out, n_keys));
672226031Sstas
673226031Sstas	for(i = 0; i < n_keys; i++){
674226031Sstas	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
675226031Sstas	    CHECK(store_data_xdr(out, new_keys[i].keyvalue));
676226031Sstas	    krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
677226031Sstas	}
678226031Sstas	free(new_keys);
679226031Sstas    }
680226031Sstas    krb5_free_principal(contextp->context, princ);
681226031Sstas}
682226031Sstas
683226031Sstasstatic void
684226031Sstasproc_init(kadm5_server_context *contextp,
685226031Sstas	  krb5_storage *in,
686226031Sstas	  krb5_storage *out)
687226031Sstas{
688226031Sstas    CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
689226031Sstas    CHECK(krb5_store_uint32(out, 0)); /* code */
690226031Sstas    CHECK(krb5_store_uint32(out, 0)); /* code */
691226031Sstas}
692226031Sstas
693226031Sstasstruct krb5_proc {
694226031Sstas    const char *name;
695226031Sstas    void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
696226031Sstas} procs[] = {
697226031Sstas    { "NULL", NULL },
698226031Sstas    { "create principal", proc_create_principal },
699226031Sstas    { "delete principal", proc_delete_principal },
700226031Sstas    { "modify principal", NULL },
701226031Sstas    { "rename principal", NULL },
702226031Sstas    { "get principal", proc_get_principal },
703226031Sstas    { "chpass principal", NULL },
704226031Sstas    { "chrand principal", proc_chrand_principal_v2 },
705226031Sstas    { "create policy", NULL },
706226031Sstas    { "delete policy", NULL },
707226031Sstas    { "modify policy", NULL },
708226031Sstas    { "get policy", NULL },
709226031Sstas    { "get privs", NULL },
710226031Sstas    { "init", proc_init },
711226031Sstas    { "get principals", NULL },
712226031Sstas    { "get polices", NULL },
713226031Sstas    { "setkey principal", NULL },
714226031Sstas    { "setkey principal v4", NULL },
715226031Sstas    { "create principal v3", NULL },
716226031Sstas    { "chpass principal v3", NULL },
717226031Sstas    { "chrand principal v3", NULL },
718226031Sstas    { "setkey principal v3", NULL }
719226031Sstas};
720226031Sstas
721226031Sstasstatic krb5_error_code
722226031Sstascopyheader(krb5_storage *sp, krb5_data *data)
723226031Sstas{
724226031Sstas    off_t off;
725226031Sstas    ssize_t sret;
726226031Sstas
727226031Sstas    off = krb5_storage_seek(sp, 0, SEEK_CUR);
728226031Sstas
729226031Sstas    CHECK(krb5_data_alloc(data, off));
730226031Sstas    INSIST((size_t)off == data->length);
731226031Sstas    krb5_storage_seek(sp, 0, SEEK_SET);
732226031Sstas    sret = krb5_storage_read(sp, data->data, data->length);
733226031Sstas    INSIST(sret == off);
734226031Sstas    INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
735226031Sstas
736226031Sstas    return 0;
737226031Sstas}
738226031Sstas
739226031Sstasstruct gctx {
740226031Sstas    krb5_data handle;
741226031Sstas    gss_ctx_id_t ctx;
742226031Sstas    uint32_t seq_num;
743226031Sstas    int done;
744226031Sstas    int inprogress;
745226031Sstas};
746226031Sstas
747226031Sstasstatic int
748226031Sstasprocess_stream(krb5_context contextp,
749226031Sstas	       unsigned char *buf, size_t ilen,
750226031Sstas	       krb5_storage *sp)
751226031Sstas{
752226031Sstas    krb5_error_code ret;
753226031Sstas    krb5_storage *msg, *reply, *dreply;
754226031Sstas    OM_uint32 maj_stat, min_stat;
755226031Sstas    gss_buffer_desc gin, gout;
756226031Sstas    struct gctx gctx;
757226031Sstas    void *server_handle = NULL;
758226031Sstas
759226031Sstas    memset(&gctx, 0, sizeof(gctx));
760226031Sstas
761226031Sstas    msg = krb5_storage_emem();
762226031Sstas    reply = krb5_storage_emem();
763226031Sstas    dreply = krb5_storage_emem();
764226031Sstas
765226031Sstas    /*
766226031Sstas     * First packet comes partly from the caller
767226031Sstas     */
768226031Sstas
769226031Sstas    INSIST(ilen >= 4);
770226031Sstas
771226031Sstas    while (1) {
772226031Sstas	struct call_header chdr;
773226031Sstas	struct gcred gcred;
774226031Sstas	uint32_t mtype;
775226031Sstas	krb5_data headercopy;
776226031Sstas
777226031Sstas	krb5_storage_truncate(dreply, 0);
778226031Sstas	krb5_storage_truncate(reply, 0);
779226031Sstas	krb5_storage_truncate(msg, 0);
780226031Sstas
781226031Sstas	krb5_data_zero(&headercopy);
782226031Sstas	memset(&chdr, 0, sizeof(chdr));
783226031Sstas	memset(&gcred, 0, sizeof(gcred));
784226031Sstas
785226031Sstas	/*
786226031Sstas	 * This is very icky to handle the the auto-detection between
787226031Sstas	 * the Heimdal protocol and the MIT ONC-RPC based protocol.
788226031Sstas	 */
789226031Sstas
790226031Sstas	if (ilen) {
791226031Sstas	    int last_fragment;
792226031Sstas	    unsigned long len;
793226031Sstas	    ssize_t slen;
794226031Sstas	    unsigned char tmp[4];
795226031Sstas
796226031Sstas	    if (ilen < 4) {
797226031Sstas		memcpy(tmp, buf, ilen);
798226031Sstas		slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
799226031Sstas		INSIST((size_t)slen == sizeof(tmp) - ilen);
800226031Sstas
801226031Sstas		ilen = sizeof(tmp);
802226031Sstas		buf = tmp;
803226031Sstas	    }
804226031Sstas	    INSIST(ilen >= 4);
805226031Sstas
806226031Sstas	    _krb5_get_int(buf, &len, 4);
807226031Sstas	    last_fragment = (len & LAST_FRAGMENT) != 0;
808226031Sstas	    len &= ~LAST_FRAGMENT;
809226031Sstas
810226031Sstas	    ilen -= 4;
811226031Sstas	    buf += 4;
812226031Sstas
813226031Sstas	    if (ilen) {
814226031Sstas		if (len < ilen) {
815226031Sstas		    slen = krb5_storage_write(msg, buf, len);
816226031Sstas		    INSIST((size_t)slen == len);
817226031Sstas		    ilen -= len;
818226031Sstas		    len = 0;
819226031Sstas		} else {
820226031Sstas		    slen = krb5_storage_write(msg, buf, ilen);
821226031Sstas		    INSIST((size_t)slen == ilen);
822226031Sstas		    len -= ilen;
823226031Sstas		}
824226031Sstas	    }
825226031Sstas
826226031Sstas	    CHECK(read_data(sp, msg, len));
827226031Sstas
828226031Sstas	    if (!last_fragment) {
829226031Sstas		ret = collect_framents(sp, msg);
830226031Sstas		if (ret == HEIM_ERR_EOF)
831226031Sstas		    krb5_errx(contextp, 0, "client disconnected");
832226031Sstas		INSIST(ret == 0);
833226031Sstas	    }
834226031Sstas	} else {
835226031Sstas
836226031Sstas	    ret = collect_framents(sp, msg);
837226031Sstas	    if (ret == HEIM_ERR_EOF)
838226031Sstas		krb5_errx(contextp, 0, "client disconnected");
839226031Sstas	    INSIST(ret == 0);
840226031Sstas	}
841226031Sstas	krb5_storage_seek(msg, 0, SEEK_SET);
842226031Sstas
843226031Sstas	CHECK(krb5_ret_uint32(msg, &chdr.xid));
844226031Sstas	CHECK(krb5_ret_uint32(msg, &mtype));
845226031Sstas	CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
846226031Sstas	CHECK(krb5_ret_uint32(msg, &chdr.prog));
847226031Sstas	CHECK(krb5_ret_uint32(msg, &chdr.vers));
848226031Sstas	CHECK(krb5_ret_uint32(msg, &chdr.proc));
849226031Sstas	CHECK(ret_auth_opaque(msg, &chdr.cred));
850226031Sstas	CHECK(copyheader(msg, &headercopy));
851226031Sstas	CHECK(ret_auth_opaque(msg, &chdr.verf));
852226031Sstas
853226031Sstas	INSIST(chdr.rpcvers == RPC_VERSION);
854226031Sstas	INSIST(chdr.prog == KADM_SERVER);
855226031Sstas	INSIST(chdr.vers == VVERSION);
856226031Sstas	INSIST(chdr.cred.flavor == FLAVOR_GSS);
857226031Sstas
858226031Sstas	CHECK(ret_gcred(&chdr.cred.data, &gcred));
859226031Sstas
860226031Sstas	INSIST(gcred.version == FLAVOR_GSS_VERSION);
861226031Sstas
862226031Sstas	if (gctx.done) {
863226031Sstas	    INSIST(chdr.verf.flavor == FLAVOR_GSS);
864226031Sstas
865226031Sstas	    /* from first byte to last of credential */
866226031Sstas	    gin.value = headercopy.data;
867226031Sstas	    gin.length = headercopy.length;
868226031Sstas	    gout.value = chdr.verf.data.data;
869226031Sstas	    gout.length = chdr.verf.data.length;
870226031Sstas
871226031Sstas	    maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
872226031Sstas	    INSIST(maj_stat == GSS_S_COMPLETE);
873226031Sstas	}
874226031Sstas
875226031Sstas	switch(gcred.proc) {
876226031Sstas	case RPG_DATA: {
877226031Sstas	    krb5_data data;
878226031Sstas	    int conf_state;
879226031Sstas	    uint32_t seq;
880226031Sstas	    krb5_storage *sp1;
881226031Sstas
882226031Sstas	    INSIST(gcred.service == rpg_privacy);
883226031Sstas
884226031Sstas	    INSIST(gctx.done);
885226031Sstas
886226031Sstas	    INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
887226031Sstas
888226031Sstas	    CHECK(ret_data_xdr(msg, &data));
889226031Sstas
890226031Sstas	    gin.value = data.data;
891226031Sstas	    gin.length = data.length;
892226031Sstas
893226031Sstas	    maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
894226031Sstas				  &conf_state, NULL);
895226031Sstas	    krb5_data_free(&data);
896226031Sstas	    INSIST(maj_stat == GSS_S_COMPLETE);
897226031Sstas	    INSIST(conf_state != 0);
898226031Sstas
899226031Sstas	    sp1 = krb5_storage_from_mem(gout.value, gout.length);
900226031Sstas	    INSIST(sp1 != NULL);
901226031Sstas
902226031Sstas	    CHECK(krb5_ret_uint32(sp1, &seq));
903226031Sstas	    INSIST (seq == gcred.seq_num);
904226031Sstas
905226031Sstas	    /*
906226031Sstas	     * Check sequence number
907226031Sstas	     */
908226031Sstas	    INSIST(seq > gctx.seq_num);
909226031Sstas	    gctx.seq_num = seq;
910226031Sstas
911226031Sstas	    /*
912226031Sstas	     * If contextp is setup, priv data have the seq_num stored
913226031Sstas	     * first in the block, so add it here before users data is
914226031Sstas	     * added.
915226031Sstas	     */
916226031Sstas	    CHECK(krb5_store_uint32(dreply, gctx.seq_num));
917226031Sstas
918226031Sstas	    if (chdr.proc >= sizeof(procs)/sizeof(procs[0])) {
919226031Sstas		krb5_warnx(contextp, "proc number out of array");
920226031Sstas	    } else if (procs[chdr.proc].func == NULL) {
921226031Sstas		krb5_warnx(contextp, "proc '%s' never implemented",
922226031Sstas			  procs[chdr.proc].name);
923226031Sstas	    } else {
924226031Sstas		krb5_warnx(contextp, "proc %s", procs[chdr.proc].name);
925226031Sstas		INSIST(server_handle != NULL);
926226031Sstas		(*procs[chdr.proc].func)(server_handle, sp, dreply);
927226031Sstas	    }
928226031Sstas	    krb5_storage_free(sp);
929226031Sstas	    gss_release_buffer(&min_stat, &gout);
930226031Sstas
931226031Sstas	    break;
932226031Sstas	}
933226031Sstas	case RPG_INIT:
934226031Sstas	    INSIST(gctx.inprogress == 0);
935226031Sstas	    INSIST(gctx.ctx == NULL);
936226031Sstas
937226031Sstas	    gctx.inprogress = 1;
938226031Sstas	    /* FALL THOUGH */
939226031Sstas	case RPG_CONTINUE_INIT: {
940226031Sstas	    gss_name_t src_name = GSS_C_NO_NAME;
941226031Sstas	    krb5_data in;
942226031Sstas
943226031Sstas	    INSIST(gctx.inprogress);
944226031Sstas
945226031Sstas	    CHECK(ret_data_xdr(msg, &in));
946226031Sstas
947226031Sstas	    gin.value = in.data;
948226031Sstas	    gin.length = in.length;
949226031Sstas	    gout.value = NULL;
950226031Sstas	    gout.length = 0;
951226031Sstas
952226031Sstas	    maj_stat = gss_accept_sec_context(&min_stat,
953226031Sstas					      &gctx.ctx,
954226031Sstas					      GSS_C_NO_CREDENTIAL,
955226031Sstas					      &gin,
956226031Sstas					      GSS_C_NO_CHANNEL_BINDINGS,
957226031Sstas					      &src_name,
958226031Sstas					      NULL,
959226031Sstas					      &gout,
960226031Sstas					      NULL,
961226031Sstas					      NULL,
962226031Sstas					      NULL);
963226031Sstas	    if (GSS_ERROR(maj_stat)) {
964226031Sstas		gss_print_errors(contextp, maj_stat, min_stat);
965226031Sstas		krb5_errx(contextp, 1, "gss error, exit");
966226031Sstas	    }
967226031Sstas	    if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
968226031Sstas		kadm5_config_params realm_params;
969226031Sstas		gss_buffer_desc bufp;
970226031Sstas		char *client;
971226031Sstas
972226031Sstas		gctx.done = 1;
973226031Sstas
974226031Sstas		memset(&realm_params, 0, sizeof(realm_params));
975226031Sstas
976226031Sstas		maj_stat = gss_export_name(&min_stat, src_name, &bufp);
977226031Sstas		INSIST(maj_stat == GSS_S_COMPLETE);
978226031Sstas
979226031Sstas		CHECK(parse_name(bufp.value, bufp.length,
980226031Sstas				 GSS_KRB5_MECHANISM, &client));
981226031Sstas
982226031Sstas		gss_release_buffer(&min_stat, &bufp);
983226031Sstas
984226031Sstas		krb5_warnx(contextp, "%s connected", client);
985226031Sstas
986226031Sstas		ret = kadm5_s_init_with_password_ctx(contextp,
987226031Sstas						     client,
988226031Sstas						     NULL,
989226031Sstas						     KADM5_ADMIN_SERVICE,
990226031Sstas						     &realm_params,
991226031Sstas						     0, 0,
992226031Sstas						     &server_handle);
993226031Sstas		INSIST(ret == 0);
994226031Sstas	    }
995226031Sstas
996226031Sstas	    INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
997226031Sstas
998226031Sstas	    CHECK(krb5_store_uint32(dreply, 0));
999226031Sstas	    CHECK(store_gss_init_res(dreply, gctx.handle,
1000226031Sstas				     maj_stat, min_stat, 1, &gout));
1001226031Sstas	    if (gout.value)
1002226031Sstas		gss_release_buffer(&min_stat, &gout);
1003226031Sstas	    if (src_name)
1004226031Sstas		gss_release_name(&min_stat, &src_name);
1005226031Sstas
1006226031Sstas	    break;
1007226031Sstas	}
1008226031Sstas	case RPG_DESTROY:
1009226031Sstas	    krb5_errx(contextp, 1, "client destroyed gss contextp");
1010226031Sstas	default:
1011226031Sstas	    krb5_errx(contextp, 1, "client sent unknown gsscode %d",
1012226031Sstas		      (int)gcred.proc);
1013226031Sstas	}
1014226031Sstas
1015226031Sstas	krb5_data_free(&gcred.handle);
1016226031Sstas	krb5_data_free(&chdr.cred.data);
1017226031Sstas	krb5_data_free(&chdr.verf.data);
1018226031Sstas	krb5_data_free(&headercopy);
1019226031Sstas
1020226031Sstas	CHECK(krb5_store_uint32(reply, chdr.xid));
1021226031Sstas	CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1022226031Sstas	CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1023226031Sstas
1024226031Sstas	if (!gctx.done) {
1025226031Sstas	    krb5_data data;
1026226031Sstas
1027226031Sstas	    CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1028226031Sstas	    CHECK(krb5_store_uint32(reply, 0)); /* length */
1029226031Sstas
1030226031Sstas	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1031226031Sstas
1032226031Sstas	    CHECK(krb5_storage_to_data(dreply, &data));
1033226031Sstas	    INSIST((size_t)krb5_storage_write(reply, data.data, data.length) == data.length);
1034226031Sstas	    krb5_data_free(&data);
1035226031Sstas
1036226031Sstas	} else {
1037226031Sstas	    uint32_t seqnum = htonl(gctx.seq_num);
1038226031Sstas	    krb5_data data;
1039226031Sstas
1040226031Sstas	    gin.value = &seqnum;
1041226031Sstas	    gin.length = sizeof(seqnum);
1042226031Sstas
1043226031Sstas	    maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
1044226031Sstas	    INSIST(maj_stat == GSS_S_COMPLETE);
1045226031Sstas
1046226031Sstas	    data.data = gout.value;
1047226031Sstas	    data.length = gout.length;
1048226031Sstas
1049226031Sstas	    CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
1050226031Sstas	    CHECK(store_data_xdr(reply, data));
1051226031Sstas	    gss_release_buffer(&min_stat, &gout);
1052226031Sstas
1053226031Sstas	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1054226031Sstas
1055226031Sstas	    CHECK(krb5_storage_to_data(dreply, &data));
1056226031Sstas
1057226031Sstas	    if (gctx.inprogress) {
1058226031Sstas		ssize_t sret;
1059226031Sstas		gctx.inprogress = 0;
1060226031Sstas		sret = krb5_storage_write(reply, data.data, data.length);
1061226031Sstas		INSIST((size_t)sret == data.length);
1062226031Sstas		krb5_data_free(&data);
1063226031Sstas	    } else {
1064226031Sstas		int conf_state;
1065226031Sstas
1066226031Sstas		gin.value = data.data;
1067226031Sstas		gin.length = data.length;
1068226031Sstas
1069226031Sstas		maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
1070226031Sstas				    &gin, &conf_state, &gout);
1071226031Sstas		INSIST(maj_stat == GSS_S_COMPLETE);
1072226031Sstas		INSIST(conf_state != 0);
1073226031Sstas		krb5_data_free(&data);
1074226031Sstas
1075226031Sstas		data.data = gout.value;
1076226031Sstas		data.length = gout.length;
1077226031Sstas
1078226031Sstas		store_data_xdr(reply, data);
1079226031Sstas		gss_release_buffer(&min_stat, &gout);
1080226031Sstas	    }
1081226031Sstas	}
1082226031Sstas
1083226031Sstas	{
1084226031Sstas	    krb5_data data;
1085226031Sstas	    ssize_t sret;
1086226031Sstas	    CHECK(krb5_storage_to_data(reply, &data));
1087226031Sstas	    CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
1088226031Sstas	    sret = krb5_storage_write(sp, data.data, data.length);
1089226031Sstas	    INSIST((size_t)sret == data.length);
1090226031Sstas	    krb5_data_free(&data);
1091226031Sstas	}
1092226031Sstas
1093226031Sstas    }
1094226031Sstas}
1095226031Sstas
1096226031Sstas
1097226031Sstasint
1098226031Sstashandle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock)
1099226031Sstas{
1100226031Sstas    krb5_storage *sp;
1101226031Sstas
1102226031Sstas    dcontext = contextp;
1103226031Sstas
1104226031Sstas    sp = krb5_storage_from_fd(sock);
1105226031Sstas    INSIST(sp != NULL);
1106226031Sstas
1107226031Sstas    process_stream(contextp, buf, len, sp);
1108226031Sstas
1109226031Sstas    return 0;
1110226031Sstas}
1111