1/*
2 * Copyright (c) 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "kadm5_locl.h"
37
38#include <syslog.h>
39#include <gssapi_krb5.h>
40
41#define CHECK(x)							\
42	do {								\
43		int __r;						\
44		if ((__r = (x))) {					\
45			syslog(LOG_ERR, "kadmin: protocol error:%d", __LINE__);			\
46			_exit(1);					\
47		}							\
48	} while(0)
49
50#define INSIST(x) CHECK(!(x))
51
52#define VERSION2 0x12345702
53
54#define LAST_FRAGMENT 0x80000000
55
56#define RPC_VERSION 2
57#define KADM_SERVER 2112
58#define VVERSION 2
59#define FLAVOR_GSS 6
60#define FLAVOR_GSS_VERSION 1
61
62#define PROC_NULL 0
63#define PROC_CREATE_PRINCIPAL 1
64#define PROC_DELETE_PRINCIPAL 2
65#define PROC_MODIFY_PRINCIPAL 3
66#define PROC_GET_PRINCIPAL 5
67#define PROC_CHRAND_PRINCIPAL 7
68#define PROC_CREATE_PRINCIPAL3 18
69
70
71struct call_header {
72    uint32_t xid;
73    uint32_t rpcvers;
74    uint32_t prog;
75    uint32_t vers;
76    uint32_t proc;
77    struct _kadm5_xdr_opaque_auth cred;
78    struct _kadm5_xdr_opaque_auth verf;
79};
80
81enum {
82    RPG_DATA = 0,
83    RPG_INIT = 1,
84    RPG_CONTINUE_INIT = 2,
85    RPG_DESTROY = 3
86};
87
88enum {
89    rpg_privacy = 3
90};
91
92#if 0
93static void
94gss_error(krb5_context context,
95	  gss_OID mech, OM_uint32 type, OM_uint32 error)
96{
97    OM_uint32 new_stat;
98    OM_uint32 msg_ctx = 0;
99    gss_buffer_desc status_string;
100    OM_uint32 ret;
101
102    do {
103	ret = gss_display_status (&new_stat,
104				  error,
105				  type,
106				  mech,
107				  &msg_ctx,
108				  &status_string);
109	krb5_warnx(context, "%.*s",
110		   (int)status_string.length,
111		   (char *)status_string.value);
112	gss_release_buffer (&new_stat, &status_string);
113    } while (!GSS_ERROR(ret) && msg_ctx != 0);
114}
115
116static void
117gss_print_errors (krb5_context context,
118		  OM_uint32 maj_stat, OM_uint32 min_stat)
119{
120    gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
121    gss_error(context, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
122}
123#endif
124
125static int
126read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
127{
128    char buf[1024];
129
130    while (len) {
131	size_t tlen = len;
132	ssize_t slen;
133
134	if (tlen > sizeof(buf))
135	    tlen = sizeof(buf);
136
137	slen = krb5_storage_read(sp, buf, tlen);
138	INSIST(slen >= 0 && (size_t)slen == tlen);
139
140	slen = krb5_storage_write(msg, buf, tlen);
141	INSIST(slen >= 0 || (size_t)slen == tlen);
142
143	len -= tlen;
144    }
145    return 0;
146}
147
148static int
149collect_fragments(krb5_storage *sp, krb5_storage *msg)
150{
151    krb5_error_code ret;
152    uint32_t len;
153    int last_fragment;
154    size_t total_len = 0;
155
156    do {
157	ret = krb5_ret_uint32(sp, &len);
158	if (ret)
159	    return ret;
160
161	last_fragment = (len & LAST_FRAGMENT);
162	len &= ~LAST_FRAGMENT;
163
164	CHECK(read_data(sp, msg, len));
165	total_len += len;
166
167    } while(!last_fragment || total_len == 0);
168
169    return 0;
170}
171
172struct grpc_client {
173    krb5_data handle;
174    krb5_data last_cred;
175    gss_ctx_id_t ctx;
176    krb5_storage *sp;
177    uint32_t seq_num;
178    uint32_t xid;
179    int done;
180    int inprogress;
181};
182
183static krb5_error_code
184xdr_close_connection(krb5_context context,
185		     struct grpc_client *client)
186{
187    free(client);
188    return 0;
189}
190
191static krb5_error_code
192xdr_send_request(krb5_context context,
193		 struct grpc_client *client,
194		 uint32_t xid,
195		 uint32_t proc,
196		 krb5_data *out)
197{
198    struct _kadm5_xdr_opaque_auth cred, verf;
199    struct _kadm5_xdr_gcred gcred;
200    OM_uint32 maj_stat, min_stat;
201    gss_buffer_desc gin, gout;
202    krb5_storage *msg;
203    krb5_data data;
204    size_t sret;
205
206    msg = krb5_storage_emem();
207
208    memset(&gcred, 0, sizeof(gcred));
209    memset(&cred, 0, sizeof(cred));
210    memset(&verf, 0, sizeof(verf));
211
212    cred.flavor = FLAVOR_GSS;
213    cred.data.data = NULL;
214    cred.data.length = 0;
215
216    gcred.version = FLAVOR_GSS_VERSION;
217    if (client->done)
218	gcred.proc = RPG_DATA;
219    else
220	gcred.proc = RPG_INIT;
221    gcred.service = rpg_privacy;
222    gcred.handle = client->handle;
223    gcred.seq_num = client->seq_num;
224
225    CHECK(_kadm5_xdr_store_gcred(&gcred, &cred.data));
226
227    CHECK(krb5_store_uint32(msg, xid));
228    CHECK(krb5_store_uint32(msg, 0)); /* mtype ? */
229    CHECK(krb5_store_uint32(msg, RPC_VERSION));
230    CHECK(krb5_store_uint32(msg, KADM_SERVER));
231    CHECK(krb5_store_uint32(msg, VVERSION));
232    CHECK(krb5_store_uint32(msg, proc));
233    CHECK(_kadm5_xdr_store_auth_opaque(msg, &cred));
234
235    /* create header verf */
236    if (client->done) {
237	krb5_storage_to_data(msg, &data);
238
239	gin.value = data.data;
240	gin.length = data.length;
241
242	maj_stat = gss_get_mic(&min_stat, client->ctx, 0, &gin, &gout);
243	krb5_data_free(&data);
244	INSIST(maj_stat == GSS_S_COMPLETE);
245
246	verf.flavor = FLAVOR_GSS;
247	verf.data.data = gout.value;
248	verf.data.length = gout.length;
249
250	CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf));
251	gss_release_buffer(&min_stat, &gout);
252    } else {
253	verf.flavor = FLAVOR_GSS;
254	verf.data.data = NULL;
255	verf.data.length = 0;
256	CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf));
257    }
258
259    if (!client->done) {
260	sret = krb5_storage_write(msg, out->data, out->length);
261	INSIST(sret == out->length);
262    } else if (client->inprogress) {
263	client->inprogress = 0;
264	sret = krb5_storage_write(msg, out->data, out->length);
265	INSIST(sret == out->length);
266    } else {
267	krb5_storage *reply;
268	int conf_state;
269
270	reply = krb5_storage_emem();
271
272	krb5_store_uint32(reply, client->seq_num);
273	sret = krb5_storage_write(reply, out->data, out->length);
274	INSIST(sret == out->length);
275
276	krb5_storage_to_data(reply, &data);
277	krb5_storage_free(reply);
278
279	gin.value = data.data;
280	gin.length = data.length;
281
282	maj_stat = gss_wrap(&min_stat, client->ctx, 1, 0,
283			    &gin, &conf_state, &gout);
284	INSIST(maj_stat == GSS_S_COMPLETE);
285	INSIST(conf_state != 0);
286	krb5_data_free(&data);
287
288	data.data = gout.value;
289	data.length = gout.length;
290
291	CHECK(_kadm5_xdr_store_data_xdr(msg, data));
292	gss_release_buffer(&min_stat, &gout);
293    }
294
295    /* write packet to output stream */
296    {
297	CHECK(krb5_storage_to_data(msg, &data));
298	CHECK(krb5_store_uint32(client->sp, ((uint32_t)data.length) | LAST_FRAGMENT));
299	sret = krb5_storage_write(client->sp, data.data, data.length);
300	INSIST(sret == data.length);
301	krb5_data_free(&data);
302    }
303
304    return 0;
305}
306
307static krb5_error_code
308xdr_recv_reply(krb5_context context,
309	       struct grpc_client *client,
310	       uint32_t *xid,
311	       krb5_storage **out)
312{
313    OM_uint32 maj_stat, min_stat;
314    gss_buffer_desc gin, gout;
315    krb5_error_code ret;
316    krb5_storage *reply;
317    krb5_data data;
318    int conf_state;
319    uint32_t tmp;
320
321    reply = krb5_storage_emem();
322    INSIST(reply != NULL);
323
324    ret = collect_fragments(client->sp, reply);
325    INSIST(ret == 0);
326
327    krb5_storage_seek(reply, 0, SEEK_SET);
328
329    CHECK(krb5_ret_uint32(reply, xid));
330    CHECK(krb5_ret_uint32(reply, &tmp)); /* REPLY */
331    INSIST(tmp == 1);
332    CHECK(krb5_ret_uint32(reply, &tmp)); /* MSG_ACCEPTED */
333    INSIST(tmp == 0);
334
335
336    CHECK(krb5_ret_uint32(reply, &tmp)); /* flavor_gss */
337    INSIST(tmp == FLAVOR_GSS);
338
339    CHECK(_kadm5_xdr_ret_data_xdr(reply, &data));
340
341    if (client->done) {
342	uint32_t seqnum = htonl(client->seq_num);
343
344	gin.value = &seqnum;
345	gin.length = sizeof(seqnum);
346	gout.value = data.data;
347	gout.length = data.length;
348
349	maj_stat = gss_verify_mic(&min_stat, client->ctx, &gin, &gout, NULL);
350	krb5_data_free(&data);
351	INSIST(maj_stat == GSS_S_COMPLETE);
352    } else {
353	krb5_data_free(&client->last_cred);
354	client->last_cred = data;
355    }
356    CHECK(krb5_ret_uint32(reply, &tmp)); /* SUCCESS */
357    INSIST(tmp == 0);
358
359    if (client->done) {
360	/* read body */
361	CHECK(_kadm5_xdr_ret_data_xdr(reply, &data));
362
363	krb5_storage_free(reply);
364
365	gin.value = data.data;
366	gin.length = data.length;
367
368	maj_stat = gss_unwrap(&min_stat, client->ctx, &gin, &gout,
369			      &conf_state, NULL);
370	krb5_data_free(&data);
371	INSIST(maj_stat == GSS_S_COMPLETE);
372	INSIST(conf_state != 0);
373
374	/* make reply cleartext */
375	*out = krb5_storage_from_mem_copy(gout.value, gout.length);
376	INSIST(*out != NULL);
377
378	/* check seq num */
379	CHECK(krb5_ret_uint32(*out, &tmp));
380	INSIST(tmp == client->seq_num);
381
382	gss_release_buffer(&min_stat, &gout);
383    } else {
384	*out = reply;
385    }
386
387    return 0;
388}
389
390static krb5_error_code
391xdr_process_request(krb5_context context,
392		    struct grpc_client *client,
393		    uint32_t proc,
394		    krb5_data *in,
395		    krb5_storage **out)
396{
397    krb5_error_code ret;
398    uint32_t xid;
399
400    client->xid++;
401    client->seq_num++;
402
403    ret = xdr_send_request(context, client, client->xid, proc, in);
404    if (ret)
405	return ret;
406    ret = xdr_recv_reply(context, client, &xid, out);
407    if (ret == 0) {
408	INSIST(client->xid == xid);
409    }
410    return ret;
411}
412
413static kadm5_ret_t
414kadm5_mit_chpass_principal(void *server_handle,
415			   krb5_principal principal,
416			   int keepold,
417			   const char *password,
418			   int n_ks_tuple,
419			   krb5_key_salt_tuple *ks_tuple)
420{
421    kadm5_mit_context *context = server_handle;
422    krb5_set_error_message(context->context, KADM5_RPC_ERROR,
423			   "Function not implemented");
424    return KADM5_RPC_ERROR;
425}
426
427static kadm5_ret_t
428kadm5_mit_chpass_principal_with_key(void *server_handle,
429				    krb5_principal princ,
430				    int keepold,
431				    int n_key_data,
432				    krb5_key_data *key_data)
433{
434    kadm5_mit_context *context = server_handle;
435    krb5_set_error_message(context->context, KADM5_RPC_ERROR,
436			   "Function not implemented");
437    return KADM5_RPC_ERROR;
438}
439
440static kadm5_ret_t
441kadm5_mit_create_principal(void *server_handle,
442			   kadm5_principal_ent_t entry,
443			   uint32_t mask,
444			   const char *password,
445			   int n_ks_tuple,
446			   krb5_key_salt_tuple *ks_tuple)
447{
448    kadm5_mit_context *context = server_handle;
449    krb5_storage *sp = NULL;
450    krb5_error_code ret;
451    uint32_t retcode;
452    krb5_data in;
453
454    /* Build request */
455
456    sp = krb5_storage_emem();
457
458    CHECK(krb5_store_uint32(sp, VERSION2));
459    CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry));
460    CHECK(krb5_store_uint32(sp, mask));
461    CHECK(_kadm5_xdr_store_string_xdr(sp, password));
462
463    krb5_storage_to_data(sp, &in);
464    krb5_storage_free(sp);
465
466    ret = xdr_process_request(context->context, context->gsscontext,
467			      PROC_CREATE_PRINCIPAL, &in, &sp);
468    krb5_data_free(&in);
469    if (ret)
470	return ret;
471
472    /* Read reply */
473
474    CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
475    INSIST(retcode == VERSION2);
476    CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
477    if (retcode == 0) {
478#if 0
479	CHECK(_kadm5_xdr_ret_principal_ent(context->context, out, ent));
480#endif
481    }
482    krb5_storage_free(sp);
483
484    return (krb5_error_code)retcode;
485}
486
487static kadm5_ret_t
488kadm5_mit_delete_principal(void *server_handle, krb5_principal principal)
489{
490    kadm5_mit_context *context = server_handle;
491    krb5_storage *sp = NULL;
492    krb5_error_code ret;
493    uint32_t retcode;
494    krb5_data in;
495
496    /* Build request */
497
498    sp = krb5_storage_emem();
499
500    CHECK(krb5_store_uint32(sp, VERSION2));
501    CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
502
503    krb5_storage_to_data(sp, &in);
504    krb5_storage_free(sp);
505
506    ret = xdr_process_request(context->context, context->gsscontext,
507			      PROC_DELETE_PRINCIPAL, &in, &sp);
508    krb5_data_free(&in);
509    if (ret)
510	return ret;
511
512    /* Read reply */
513
514    CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
515    INSIST(retcode == VERSION2);
516    CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
517    krb5_storage_free(sp);
518
519    return (krb5_error_code)retcode;
520}
521
522static kadm5_ret_t
523kadm5_mit_flush(void *server_handle)
524{
525    kadm5_mit_context *context = server_handle;
526    krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
527    return KADM5_RPC_ERROR;
528}
529
530static kadm5_ret_t
531kadm5_mit_destroy(void *server_handle)
532{
533    kadm5_mit_context *context = server_handle;
534
535    krb5_free_principal(context->context, context->caller);
536    xdr_close_connection(context->context, context->gsscontext);
537    if(context->my_context)
538	krb5_free_context(context->context);
539    return 0;
540}
541
542static kadm5_ret_t
543kadm5_mit_get_principal(void *server_handle,
544			krb5_principal principal,
545			kadm5_principal_ent_t entry,
546			uint32_t mask)
547{
548    kadm5_mit_context *context = server_handle;
549    krb5_storage *sp = NULL;
550    krb5_error_code ret;
551    uint32_t retcode;
552    krb5_data in;
553
554    /* Build request */
555
556    sp = krb5_storage_emem();
557
558    CHECK(krb5_store_uint32(sp, VERSION2));
559    CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
560    CHECK(krb5_store_uint32(sp, mask));
561
562    krb5_storage_to_data(sp, &in);
563    krb5_storage_free(sp);
564
565    ret = xdr_process_request(context->context, context->gsscontext,
566			      PROC_GET_PRINCIPAL, &in, &sp);
567    krb5_data_free(&in);
568    if (ret)
569	return ret;
570
571    /* Read reply */
572
573    CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
574    INSIST(retcode == VERSION2);
575    CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
576    if (retcode == 0) {
577	CHECK(_kadm5_xdr_ret_principal_ent(context->context, sp, entry));
578    }
579    krb5_storage_free(sp);
580
581    return (krb5_error_code)retcode;
582}
583
584static kadm5_ret_t
585kadm5_mit_get_principals(void *server_handle,
586			 const char *expression,
587			 char ***principals,
588			 int *count)
589{
590    kadm5_mit_context *context = server_handle;
591    krb5_set_error_message(context->context, KADM5_RPC_ERROR,
592			   "Function not implemented");
593    return KADM5_RPC_ERROR;
594}
595
596static kadm5_ret_t
597kadm5_mit_get_privs(void *server_handle, uint32_t*privs)
598{
599    kadm5_mit_context *context = server_handle;
600    krb5_set_error_message(context->context, KADM5_RPC_ERROR,
601			   "Function not implemented");
602    return KADM5_RPC_ERROR;
603}
604
605static kadm5_ret_t
606kadm5_mit_modify_principal(void *server_handle,
607			  kadm5_principal_ent_t entry,
608			  uint32_t mask)
609{
610    kadm5_mit_context *context = server_handle;
611    krb5_storage *sp = NULL;
612    krb5_error_code ret;
613    uint32_t retcode;
614    krb5_data in;
615
616    /* Build request */
617
618    sp = krb5_storage_emem();
619
620    CHECK(krb5_store_uint32(sp, VERSION2));
621    CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry));
622    CHECK(krb5_store_uint32(sp, mask));
623
624    krb5_storage_to_data(sp, &in);
625    krb5_storage_free(sp);
626
627    ret = xdr_process_request(context->context, context->gsscontext,
628			      PROC_MODIFY_PRINCIPAL, &in, &sp);
629    krb5_data_free(&in);
630    if (ret)
631	return ret;
632
633    /* Read reply */
634
635    CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
636#if 0 /* modify doesn't set api version */
637    INSIST(retcode == VERSION2);
638#endif
639    CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
640    krb5_storage_free(sp);
641
642    return (krb5_error_code)retcode;
643}
644
645static kadm5_ret_t
646kadm5_mit_rename_principal(void *server_handle,
647			  krb5_principal from,
648			  krb5_principal to)
649{
650    kadm5_mit_context *context = server_handle;
651    krb5_set_error_message(context->context, KADM5_RPC_ERROR,
652			   "Function not implemented");
653    return KADM5_RPC_ERROR;
654}
655
656static kadm5_ret_t
657kadm5_mit_randkey_principal(void *server_handle,
658			    krb5_principal principal,
659			    krb5_boolean keepold,
660			    int n_ks_tuple,
661			    krb5_key_salt_tuple *ks_tuple,
662			    krb5_keyblock **keys,
663			    int *n_keys)
664{
665    kadm5_mit_context *context = server_handle;
666    krb5_storage *sp = NULL;
667    krb5_error_code ret;
668    uint32_t retcode;
669    krb5_data in;
670
671    /* Build request */
672
673    sp = krb5_storage_emem();
674
675    CHECK(krb5_store_uint32(sp, VERSION2));
676    CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
677
678    krb5_storage_to_data(sp, &in);
679    krb5_storage_free(sp);
680
681    ret = xdr_process_request(context->context, context->gsscontext,
682			      PROC_CHRAND_PRINCIPAL, &in, &sp);
683    krb5_data_free(&in);
684    if (ret)
685	return ret;
686
687    /* Read reply */
688
689    CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
690    INSIST(retcode == VERSION2);
691    CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
692    if (retcode == 0) {
693	uint32_t enctype, num;
694	int i;
695
696	CHECK(krb5_ret_uint32(sp, &num));
697	CHECK(num < 2000);
698
699	*n_keys = num;
700
701	*keys = calloc(num, sizeof((*keys)[0]));
702	for (i = 0; i < *n_keys; i++) {
703	    CHECK(krb5_ret_uint32(sp, &enctype));
704	    (*keys)[i].keytype = enctype;
705	    CHECK(_kadm5_xdr_ret_data_xdr(sp, &(*keys)[i].keyvalue));
706	}
707    }
708    krb5_storage_free(sp);
709
710    return (krb5_error_code)retcode;
711}
712
713/*
714 *
715 */
716
717static void
718verify_seq_num(struct grpc_client *c, uint32_t seq, krb5_data *cred)
719{
720    OM_uint32 maj_stat, min_stat;
721    gss_buffer_desc gin, gout;
722
723    seq = htonl(seq);
724
725    gin.value = &seq;
726    gin.length = sizeof(seq);
727    gout.value = cred->data;
728    gout.length = cred->length;
729
730    INSIST(cred->length != 0);
731
732    maj_stat = gss_verify_mic(&min_stat, c->ctx, &gin, &gout, NULL);
733    krb5_data_free(&c->last_cred);
734    INSIST(maj_stat == GSS_S_COMPLETE);
735}
736
737
738static krb5_error_code
739xdr_setup_connection(krb5_context context,
740		     const char *service,
741		     const char *hostname,
742		     unsigned port,
743		     struct grpc_client **client)
744{
745    struct addrinfo *ai, *a, hints;
746    char portstr[NI_MAXSERV];
747    struct grpc_client *c;
748    krb5_error_code ret;
749    int error, s = -1;
750    OM_uint32 maj_stat, maj_stat2, min_stat, ret_flags;
751    gss_buffer_desc gin, gout;
752    gss_name_t target;
753    int try_kadmin_admin = 0;
754    uint32_t seq_window = 0;
755
756    c = calloc(1, sizeof(*c));
757    if (c == NULL)
758	return ENOMEM;
759
760    memset (&hints, 0, sizeof(hints));
761    hints.ai_socktype = SOCK_STREAM;
762    hints.ai_protocol = IPPROTO_TCP;
763
764    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
765
766    error = getaddrinfo (hostname, portstr, &hints, &ai);
767    if (error)
768	return KADM5_BAD_SERVER_NAME;
769
770    for (a = ai; a != NULL; a = a->ai_next) {
771	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
772	if (s < 0)
773	    continue;
774	socket_set_nopipe(s, 1);
775	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
776	    close (s);
777	    continue;
778	}
779	break;
780    }
781    if (a == NULL || s < 0) {
782	freeaddrinfo (ai);
783	return KADM5_FAILURE;
784    }
785
786    c->sp = krb5_storage_from_fd(s);
787    close(s);
788
789 try_again:
790    {
791	char *str;
792	if (!try_kadmin_admin)
793	    asprintf(&str, "%s@%s", service, hostname);
794	else
795	    asprintf(&str, "kadmin@admin");
796	gin.value = str;
797	gin.length = strlen(str);
798	maj_stat = gss_import_name(&min_stat, &gin, GSS_C_NT_HOSTBASED_SERVICE, &target);
799	if (maj_stat != GSS_S_COMPLETE) {
800	    krb5_set_error_message(context, ENOENT,
801				   "failed to import name: %s", str);
802	    free(str);
803	    return ENOENT;
804	}
805	free(str);
806    }
807
808
809    c->inprogress = 1;
810    gin.value = NULL;
811    gin.length = 0;
812
813    /* do gss-api negotiation dance here */
814    while (1) {
815	krb5_storage *out, *in = krb5_storage_emem();
816	krb5_data data;
817
818	maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL,
819					&c->ctx, target, GSS_KRB5_MECHANISM,
820					GSS_C_MUTUAL_FLAG|GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
821					GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
822					&gin, NULL, &gout, &ret_flags, NULL);
823	if (gin.value) {
824	    free(gin.value);
825	    gin.value = NULL;
826	}
827
828	if (GSS_ERROR(maj_stat)) {
829	    krb5_storage_free(in);
830	    if (!try_kadmin_admin) {
831		try_kadmin_admin = 1;
832		goto try_again;
833	    }
834	    krb5_set_error_message(context, EINVAL,
835				   "init_sec_context failed with %d/%d",
836				   maj_stat, min_stat);
837	    return EINVAL;
838	} else if (maj_stat & GSS_S_CONTINUE_NEEDED) {
839	    INSIST(!c->done);
840	} else {
841	    INSIST(maj_stat == GSS_S_COMPLETE);
842	    INSIST((ret_flags & GSS_C_MUTUAL_FLAG) != 0);
843	    if (c->done) {
844		verify_seq_num(c, seq_window, &c->last_cred);
845		krb5_data_free(&c->last_cred);
846		break;
847	    }
848
849	    c->done = 1;
850	}
851
852	data.data = gout.value;
853	data.length = gout.length;
854
855	CHECK(_kadm5_xdr_store_data_xdr(in, data));
856	gss_release_buffer(&min_stat, &gout);
857	krb5_storage_to_data(in, &data);
858	krb5_storage_free(in);
859
860	ret = xdr_process_request(context, c, PROC_NULL, &data, &out);
861	krb5_data_free(&data);
862	if (ret)
863	    return ret;
864
865	CHECK(_kadm5_xdr_ret_gss_init_res(out, &c->handle,
866					  &maj_stat2, &min_stat,
867					  &seq_window, &data));
868	krb5_storage_free(out);
869
870	gin.length = data.length;
871	gin.value = data.data;
872
873	if (GSS_ERROR(maj_stat2)) {
874	    krb5_data_free(&data);
875	    krb5_set_error_message(context, EINVAL,
876				   "server sent a failure code: %d/%d",
877				   maj_stat2, min_stat);
878	    return EINVAL;
879	} else if (maj_stat2 & GSS_S_CONTINUE_NEEDED) {
880	    INSIST(!c->done);
881
882	} else {
883	    INSIST(maj_stat2 == GSS_S_COMPLETE);
884	    if (c->done) {
885		verify_seq_num(c, seq_window, &c->last_cred);
886		krb5_data_free(&c->last_cred);
887		break;
888	    }
889	    c->done = 1;
890	}
891    }
892
893    c->inprogress = 0;
894
895    *client = c;
896    return 0;
897}
898
899
900static void
901set_funcs(kadm5_mit_context *c)
902{
903#define SET(C, F) (C)->funcs.F = kadm5_mit_ ## F
904    SET(c, chpass_principal);
905    SET(c, chpass_principal_with_key);
906    SET(c, create_principal);
907    SET(c, delete_principal);
908    SET(c, destroy);
909    SET(c, flush);
910    SET(c, get_principal);
911    SET(c, get_principals);
912    SET(c, get_privs);
913    SET(c, modify_principal);
914    SET(c, randkey_principal);
915    SET(c, rename_principal);
916}
917
918/*
919 *
920 */
921
922kadm5_ret_t
923kadm5_mit_init_with_password_ctx(krb5_context context,
924				 const char *client_name,
925				 const char *password,
926				 kadm5_config_params *params,
927				 unsigned long struct_version,
928				 unsigned long api_version,
929				 void **server_handle)
930{
931    kadm5_ret_t ret;
932    kadm5_mit_context *ctx;
933    struct grpc_client *c = NULL;
934    char *colon;
935
936    ctx = malloc(sizeof(*ctx));
937    if(ctx == NULL)
938	return ENOMEM;
939    memset(ctx, 0, sizeof(*ctx));
940    set_funcs(ctx);
941
942    ctx->context = context;
943    ctx->my_context = 0;
944    krb5_add_et_list (context, initialize_kadm5_error_table_r);
945
946    if (client_name) {
947	ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
948    } else {
949	krb5_ccache id;
950
951	ret = _kadm5_c_get_cache_principal(context, &id, &ctx->caller);
952	if (ret) {
953	    const char *user;
954
955	    user = get_default_username ();
956	    if(user == NULL) {
957		krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
958		ret = KADM5_FAILURE;
959	    } else {
960		ret = krb5_make_principal(context, &ctx->caller,
961					  NULL, user, "admin", NULL);
962	    }
963	} else if (id) {
964	    krb5_cc_close(context, id);
965	}
966    }
967    if(ret) {
968	free(ctx);
969	return ret;
970    }
971
972    if(params->mask & KADM5_CONFIG_REALM) {
973	ret = 0;
974	ctx->realm = strdup(params->realm);
975	if (ctx->realm == NULL)
976	    ret = ENOMEM;
977    } else
978	ret = krb5_get_default_realm(ctx->context, &ctx->realm);
979    if (ret) {
980	free(ctx);
981	return ret;
982    }
983
984    if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
985	ctx->admin_server = strdup(params->admin_server);
986    else {
987	krb5_krbhst_handle handle = NULL;
988	char host[MAXHOSTNAMELEN];
989
990	ret = krb5_krbhst_init(context, ctx->realm, KRB5_KRBHST_ADMIN, &handle);
991	if (ret == 0)
992	    ret = krb5_krbhst_next_as_string(context, handle, host, sizeof(host));
993	krb5_krbhst_free(context, handle);
994	if (ret) {
995	    free(ctx->realm);
996	    free(ctx);
997	    return ret;
998	}
999	ctx->admin_server = strdup(host);
1000    }
1001
1002    if (ctx->admin_server == NULL) {
1003	free(ctx->realm);
1004	free(ctx);
1005	return ENOMEM;
1006    }
1007    colon = strchr (ctx->admin_server, ':');
1008    if (colon != NULL)
1009	*colon++ = '\0';
1010
1011    ctx->kadmind_port = 0;
1012
1013    if(params->mask & KADM5_CONFIG_KADMIND_PORT)
1014	ctx->kadmind_port = params->kadmind_port;
1015    else if (colon != NULL) {
1016	char *end;
1017	ctx->kadmind_port = htons(strtol (colon, &end, 0));
1018    }
1019    if (ctx->kadmind_port == 0)
1020	ctx->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
1021						   "tcp", 749);
1022
1023    ret = xdr_setup_connection(context, KADM5_KADMIN_SERVICE,
1024			       ctx->admin_server, ctx->kadmind_port, &c);
1025    if (ret) {
1026	free(ctx->realm);
1027	free(ctx);
1028	return ret;
1029    }
1030
1031    ctx->gsscontext = c;
1032
1033    *server_handle = ctx;
1034    return 0;
1035}
1036