1/*
2 * Copyright (c) 2006 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 KTH nor the names of its contributors may be
18 *    used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "common.h"
35RCSID("$Id$");
36
37/*
38 *
39 */
40
41enum handle_type { handle_context, handle_cred };
42
43struct handle {
44    int32_t idx;
45    enum handle_type type;
46    void *ptr;
47    struct handle *next;
48};
49
50struct client {
51    krb5_storage *sock;
52    krb5_storage *logging;
53    char *moniker;
54    int32_t nHandle;
55    struct handle *handles;
56    struct sockaddr_storage sa;
57    socklen_t salen;
58    char servername[MAXHOSTNAMELEN];
59};
60
61FILE *logfile;
62static char *targetname;
63krb5_context context;
64
65/*
66 *
67 */
68
69static void
70logmessage(struct client *c, const char *file, unsigned int lineno,
71	   int level, const char *fmt, ...)
72{
73    char *message;
74    va_list ap;
75    int32_t ackid;
76
77    va_start(ap, fmt);
78    vasprintf(&message, fmt, ap);
79    va_end(ap);
80
81    if (logfile)
82	fprintf(logfile, "%s:%u: %d %s\n", file, lineno, level, message);
83
84    if (c->logging) {
85	if (krb5_store_int32(c->logging, eLogInfo) != 0)
86	    errx(1, "krb5_store_int32: log level");
87	if (krb5_store_string(c->logging, file) != 0)
88	    errx(1, "krb5_store_string: filename");
89	if (krb5_store_int32(c->logging, lineno) != 0)
90	    errx(1, "krb5_store_string: filename");
91	if (krb5_store_string(c->logging, message) != 0)
92	    errx(1, "krb5_store_string: message");
93	if (krb5_ret_int32(c->logging, &ackid) != 0)
94	    errx(1, "krb5_ret_int32: ackid");
95    }
96    free(message);
97}
98
99/*
100 *
101 */
102
103static int32_t
104add_handle(struct client *c, enum handle_type type, void *data)
105{
106    struct handle *h;
107
108    h = ecalloc(1, sizeof(*h));
109
110    h->idx = ++c->nHandle;
111    h->type = type;
112    h->ptr = data;
113    h->next = c->handles;
114    c->handles = h;
115
116    return h->idx;
117}
118
119static void
120del_handle(struct handle **h, int32_t idx)
121{
122    OM_uint32 min_stat;
123
124    if (idx == 0)
125	return;
126
127    while (*h) {
128	if ((*h)->idx == idx) {
129	    struct handle *p = *h;
130	    *h = (*h)->next;
131	    switch(p->type) {
132	    case handle_context: {
133		gss_ctx_id_t c = p->ptr;
134		gss_delete_sec_context(&min_stat, &c, NULL);
135		break; }
136	    case handle_cred: {
137		gss_cred_id_t c = p->ptr;
138		gss_release_cred(&min_stat, &c);
139		break; }
140	    }
141	    free(p);
142	    return;
143	}
144	h = &((*h)->next);
145    }
146    errx(1, "tried to delete an unexisting handle");
147}
148
149static void *
150find_handle(struct handle *h, int32_t idx, enum handle_type type)
151{
152    if (idx == 0)
153	return NULL;
154
155    while (h) {
156	if (h->idx == idx) {
157	    if (type == h->type)
158		return h->ptr;
159	    errx(1, "monger switched type on handle!");
160	}
161	h = h->next;
162    }
163    return NULL;
164}
165
166
167static int32_t
168convert_gss_to_gsm(OM_uint32 maj_stat)
169{
170    switch(maj_stat) {
171    case 0:
172	return GSMERR_OK;
173    case GSS_S_CONTINUE_NEEDED:
174	return GSMERR_CONTINUE_NEEDED;
175    case GSS_S_DEFECTIVE_TOKEN:
176        return GSMERR_INVALID_TOKEN;
177    case GSS_S_BAD_MIC:
178	return GSMERR_AP_MODIFIED;
179    default:
180	return GSMERR_ERROR;
181    }
182}
183
184static int32_t
185convert_krb5_to_gsm(krb5_error_code ret)
186{
187    switch(ret) {
188    case 0:
189	return GSMERR_OK;
190    default:
191	return GSMERR_ERROR;
192    }
193}
194
195/*
196 *
197 */
198
199static int32_t
200acquire_cred(struct client *c,
201	     krb5_principal principal,
202	     krb5_get_init_creds_opt *opt,
203	     int32_t *handle)
204{
205    krb5_error_code ret;
206    krb5_creds cred;
207    krb5_ccache id;
208    gss_cred_id_t gcred;
209    OM_uint32 maj_stat, min_stat;
210
211    *handle = 0;
212
213    krb5_get_init_creds_opt_set_forwardable (opt, 1);
214    krb5_get_init_creds_opt_set_renew_life (opt, 3600 * 24 * 30);
215
216    memset(&cred, 0, sizeof(cred));
217
218    ret = krb5_get_init_creds_password (context,
219					&cred,
220					principal,
221					NULL,
222					NULL,
223					NULL,
224					0,
225					NULL,
226					opt);
227    if (ret) {
228	logmessage(c, __FILE__, __LINE__, 0,
229		   "krb5_get_init_creds failed: %d", ret);
230	return convert_krb5_to_gsm(ret);
231    }
232
233    ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);
234    if (ret)
235	krb5_err (context, 1, ret, "krb5_cc_initialize");
236
237    ret = krb5_cc_initialize (context, id, cred.client);
238    if (ret)
239	krb5_err (context, 1, ret, "krb5_cc_initialize");
240
241    ret = krb5_cc_store_cred (context, id, &cred);
242    if (ret)
243	krb5_err (context, 1, ret, "krb5_cc_store_cred");
244
245    krb5_free_cred_contents (context, &cred);
246
247    maj_stat = gss_krb5_import_cred(&min_stat,
248				    id,
249				    NULL,
250				    NULL,
251				    &gcred);
252    krb5_cc_close(context, id);
253    if (maj_stat) {
254	logmessage(c, __FILE__, __LINE__, 0,
255		   "krb5 import creds failed with: %d", maj_stat);
256	return convert_gss_to_gsm(maj_stat);
257    }
258
259    *handle = add_handle(c, handle_cred, gcred);
260
261    return 0;
262}
263
264
265/*
266 *
267 */
268
269#define HandleOP(h) \
270handle##h(enum gssMaggotOp op, struct client *c)
271
272/*
273 *
274 */
275
276static int
277HandleOP(GetVersionInfo)
278{
279    put32(c, GSSMAGGOTPROTOCOL);
280    errx(1, "GetVersionInfo");
281}
282
283static int
284HandleOP(GoodBye)
285{
286    struct handle *h = c->handles;
287    unsigned int i = 0;
288
289    while (h) {
290	h = h->next;
291	i++;
292    }
293
294    if (i)
295	logmessage(c, __FILE__, __LINE__, 0,
296		   "Did not toast all resources: %d", i);
297    return 1;
298}
299
300static int
301HandleOP(InitContext)
302{
303    OM_uint32 maj_stat, min_stat, ret_flags;
304    int32_t hContext, hCred, flags;
305    krb5_data target_name, in_token;
306    int32_t new_context_id = 0, gsm_error = 0;
307    krb5_data out_token = { 0 , NULL };
308
309    gss_ctx_id_t ctx;
310    gss_cred_id_t creds;
311    gss_name_t gss_target_name;
312    gss_buffer_desc input_token, output_token;
313    gss_OID oid = GSS_C_NO_OID;
314    gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
315
316    ret32(c, hContext);
317    ret32(c, hCred);
318    ret32(c, flags);
319    retdata(c, target_name);
320    retdata(c, in_token);
321
322    logmessage(c, __FILE__, __LINE__, 0,
323	       "targetname: <%.*s>", (int)target_name.length,
324	       (char *)target_name.data);
325
326    ctx = find_handle(c->handles, hContext, handle_context);
327    if (ctx == NULL)
328	hContext = 0;
329    creds = find_handle(c->handles, hCred, handle_cred);
330    if (creds == NULL)
331	abort();
332
333    input_token.length = target_name.length;
334    input_token.value = target_name.data;
335
336    maj_stat = gss_import_name(&min_stat,
337			       &input_token,
338			       GSS_KRB5_NT_PRINCIPAL_NAME,
339			       &gss_target_name);
340    if (GSS_ERROR(maj_stat)) {
341	logmessage(c, __FILE__, __LINE__, 0,
342		   "import name creds failed with: %d", maj_stat);
343	gsm_error = convert_gss_to_gsm(maj_stat);
344	goto out;
345    }
346
347    /* oid from flags */
348
349    if (in_token.length) {
350	input_token.length = in_token.length;
351	input_token.value = in_token.data;
352	input_token_ptr = &input_token;
353	if (ctx == NULL)
354	    krb5_errx(context, 1, "initcreds, context NULL, but not first req");
355    } else {
356	input_token.length = 0;
357	input_token.value = NULL;
358	if (ctx)
359	    krb5_errx(context, 1, "initcreds, context not NULL, but first req");
360    }
361
362    if ((flags & GSS_C_DELEG_FLAG) != 0)
363	logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating");
364    if ((flags & GSS_C_DCE_STYLE) != 0)
365	logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style");
366
367    maj_stat = gss_init_sec_context(&min_stat,
368				    creds,
369				    &ctx,
370				    gss_target_name,
371				    oid,
372				    flags & 0x7f,
373				    0,
374				    NULL,
375				    input_token_ptr,
376				    NULL,
377				    &output_token,
378				    &ret_flags,
379				    NULL);
380    if (GSS_ERROR(maj_stat)) {
381	if (hContext != 0)
382	    del_handle(&c->handles, hContext);
383	new_context_id = 0;
384	logmessage(c, __FILE__, __LINE__, 0,
385		   "gss_init_sec_context returns code: %d/%d",
386		   maj_stat, min_stat);
387    } else {
388	if (input_token.length == 0)
389	    new_context_id = add_handle(c, handle_context, ctx);
390	else
391	    new_context_id = hContext;
392    }
393
394    gsm_error = convert_gss_to_gsm(maj_stat);
395
396    if (output_token.length) {
397	out_token.data = output_token.value;
398	out_token.length = output_token.length;
399    }
400
401out:
402    logmessage(c, __FILE__, __LINE__, 0,
403	       "InitContext return code: %d", gsm_error);
404
405    put32(c, new_context_id);
406    put32(c, gsm_error);
407    putdata(c, out_token);
408
409    gss_release_name(&min_stat, &gss_target_name);
410    if (output_token.length)
411	gss_release_buffer(&min_stat, &output_token);
412    krb5_data_free(&in_token);
413    krb5_data_free(&target_name);
414
415    return 0;
416}
417
418static int
419HandleOP(AcceptContext)
420{
421    OM_uint32 maj_stat, min_stat, ret_flags;
422    int32_t hContext, deleg_hcred, flags;
423    krb5_data in_token;
424    int32_t new_context_id = 0, gsm_error = 0;
425    krb5_data out_token = { 0 , NULL };
426
427    gss_ctx_id_t ctx;
428    gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL;
429    gss_buffer_desc input_token, output_token;
430    gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
431
432    ret32(c, hContext);
433    ret32(c, flags);
434    retdata(c, in_token);
435
436    ctx = find_handle(c->handles, hContext, handle_context);
437    if (ctx == NULL)
438	hContext = 0;
439
440    if (in_token.length) {
441	input_token.length = in_token.length;
442	input_token.value = in_token.data;
443	input_token_ptr = &input_token;
444    } else {
445	input_token.length = 0;
446	input_token.value = NULL;
447    }
448
449    maj_stat = gss_accept_sec_context(&min_stat,
450				      &ctx,
451				      GSS_C_NO_CREDENTIAL,
452				      &input_token,
453				      GSS_C_NO_CHANNEL_BINDINGS,
454				      NULL,
455				      NULL,
456				      &output_token,
457				      &ret_flags,
458				      NULL,
459				      &deleg_cred);
460    if (GSS_ERROR(maj_stat)) {
461	if (hContext != 0)
462	    del_handle(&c->handles, hContext);
463	logmessage(c, __FILE__, __LINE__, 0,
464		   "gss_accept_sec_context returns code: %d/%d",
465		   maj_stat, min_stat);
466	new_context_id = 0;
467    } else {
468	if (hContext == 0)
469	    new_context_id = add_handle(c, handle_context, ctx);
470	else
471	    new_context_id = hContext;
472    }
473    if (output_token.length) {
474	out_token.data = output_token.value;
475	out_token.length = output_token.length;
476    }
477    if ((ret_flags & GSS_C_DCE_STYLE) != 0)
478	logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style");
479    if ((ret_flags & GSS_C_DELEG_FLAG) != 0) {
480	deleg_hcred = add_handle(c, handle_cred, deleg_cred);
481	logmessage(c, __FILE__, __LINE__, 0,
482		   "accept_context delegated handle: %d", deleg_hcred);
483    } else {
484	gss_release_cred(&min_stat, &deleg_cred);
485	deleg_hcred = 0;
486    }
487
488
489    gsm_error = convert_gss_to_gsm(maj_stat);
490
491    put32(c, new_context_id);
492    put32(c, gsm_error);
493    putdata(c, out_token);
494    put32(c, deleg_hcred);
495
496    if (output_token.length)
497	gss_release_buffer(&min_stat, &output_token);
498    krb5_data_free(&in_token);
499
500    return 0;
501}
502
503static int
504HandleOP(ToastResource)
505{
506    int32_t handle;
507
508    ret32(c, handle);
509    logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle);
510    del_handle(&c->handles, handle);
511    put32(c, GSMERR_OK);
512
513    return 0;
514}
515
516static int
517HandleOP(AcquireCreds)
518{
519    char *name, *password;
520    int32_t gsm_error, flags, handle = 0;
521    krb5_principal principal = NULL;
522    krb5_get_init_creds_opt *opt = NULL;
523    krb5_error_code ret;
524
525    retstring(c, name);
526    retstring(c, password);
527    ret32(c, flags);
528
529    logmessage(c, __FILE__, __LINE__, 0,
530	       "username: %s password: %s", name, password);
531
532    ret = krb5_parse_name(context, name, &principal);
533    if (ret) {
534	gsm_error = convert_krb5_to_gsm(ret);
535	goto out;
536    }
537
538    ret = krb5_get_init_creds_opt_alloc (context, &opt);
539    if (ret)
540	krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
541
542    krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL);
543
544    gsm_error = acquire_cred(c, principal, opt, &handle);
545
546out:
547    logmessage(c, __FILE__, __LINE__, 0,
548	       "AcquireCreds handle: %d return code: %d", handle, gsm_error);
549
550    if (opt)
551	krb5_get_init_creds_opt_free (context, opt);
552    if (principal)
553	krb5_free_principal(context, principal);
554    free(name);
555    free(password);
556
557    put32(c, gsm_error);
558    put32(c, handle);
559
560    return 0;
561}
562
563static int
564HandleOP(Sign)
565{
566    OM_uint32 maj_stat, min_stat;
567    int32_t hContext, flags, seqno;
568    krb5_data token;
569    gss_ctx_id_t ctx;
570    gss_buffer_desc input_token, output_token;
571
572    ret32(c, hContext);
573    ret32(c, flags);
574    ret32(c, seqno);
575    retdata(c, token);
576
577    ctx = find_handle(c->handles, hContext, handle_context);
578    if (ctx == NULL)
579	errx(1, "sign: reference to unknown context");
580
581    input_token.length = token.length;
582    input_token.value = token.data;
583
584    maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token,
585			   &output_token);
586    if (maj_stat != GSS_S_COMPLETE)
587	errx(1, "gss_get_mic failed");
588
589    krb5_data_free(&token);
590
591    token.data = output_token.value;
592    token.length = output_token.length;
593
594    put32(c, 0); /* XXX fix gsm_error */
595    putdata(c, token);
596
597    gss_release_buffer(&min_stat, &output_token);
598
599    return 0;
600}
601
602static int
603HandleOP(Verify)
604{
605    OM_uint32 maj_stat, min_stat;
606    int32_t hContext, flags, seqno;
607    krb5_data msg, mic;
608    gss_ctx_id_t ctx;
609    gss_buffer_desc msg_token, mic_token;
610    gss_qop_t qop;
611
612    ret32(c, hContext);
613
614    ctx = find_handle(c->handles, hContext, handle_context);
615    if (ctx == NULL)
616	errx(1, "verify: reference to unknown context");
617
618    ret32(c, flags);
619    ret32(c, seqno);
620    retdata(c, msg);
621
622    msg_token.length = msg.length;
623    msg_token.value = msg.data;
624
625    retdata(c, mic);
626
627    mic_token.length = mic.length;
628    mic_token.value = mic.data;
629
630    maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token,
631			      &mic_token, &qop);
632    if (maj_stat != GSS_S_COMPLETE)
633	errx(1, "gss_verify_mic failed");
634
635    krb5_data_free(&mic);
636    krb5_data_free(&msg);
637
638    put32(c, 0); /* XXX fix gsm_error */
639
640    return 0;
641}
642
643static int
644HandleOP(GetVersionAndCapabilities)
645{
646    int32_t cap = HAS_MONIKER;
647    char name[256] = "unknown", *str;
648
649    if (targetname)
650	cap |= ISSERVER; /* is server */
651
652#ifdef HAVE_UNAME
653    {
654	struct utsname ut;
655	if (uname(&ut) == 0) {
656	    snprintf(name, sizeof(name), "%s-%s-%s",
657		     ut.sysname, ut.version, ut.machine);
658	}
659    }
660#endif
661
662    asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name);
663
664    put32(c, GSSMAGGOTPROTOCOL);
665    put32(c, cap);
666    putstring(c, str);
667    free(str);
668
669    return 0;
670}
671
672static int
673HandleOP(GetTargetName)
674{
675    if (targetname)
676	putstring(c, targetname);
677    else
678	putstring(c, "");
679    return 0;
680}
681
682static int
683HandleOP(SetLoggingSocket)
684{
685    int32_t portnum;
686    int fd, ret;
687
688    ret32(c, portnum);
689
690    logmessage(c, __FILE__, __LINE__, 0,
691	       "logging port on peer is: %d", (int)portnum);
692
693    socket_set_port((struct sockaddr *)(&c->sa), htons(portnum));
694
695    fd = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0);
696    if (fd < 0)
697	return 0;
698
699    ret = connect(fd, (struct sockaddr *)&c->sa, c->salen);
700    if (ret < 0) {
701	logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s",
702		   strerror(errno));
703	close(fd);
704	return 0;
705    }
706
707    if (c->logging)
708	krb5_storage_free(c->logging);
709    c->logging = krb5_storage_from_fd(fd);
710    close(fd);
711
712    krb5_store_int32(c->logging, eLogSetMoniker);
713    store_string(c->logging, c->moniker);
714
715    logmessage(c, __FILE__, __LINE__, 0, "logging turned on");
716
717    return 0;
718}
719
720
721static int
722HandleOP(ChangePassword)
723{
724    errx(1, "ChangePassword");
725}
726
727static int
728HandleOP(SetPasswordSelf)
729{
730    errx(1, "SetPasswordSelf");
731}
732
733static int
734HandleOP(Wrap)
735{
736    OM_uint32 maj_stat, min_stat;
737    int32_t hContext, flags, seqno;
738    krb5_data token;
739    gss_ctx_id_t ctx;
740    gss_buffer_desc input_token, output_token;
741    int conf_state;
742
743    ret32(c, hContext);
744    ret32(c, flags);
745    ret32(c, seqno);
746    retdata(c, token);
747
748    ctx = find_handle(c->handles, hContext, handle_context);
749    if (ctx == NULL)
750	errx(1, "wrap: reference to unknown context");
751
752    input_token.length = token.length;
753    input_token.value = token.data;
754
755    maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token,
756			&conf_state, &output_token);
757    if (maj_stat != GSS_S_COMPLETE)
758	errx(1, "gss_wrap failed");
759
760    krb5_data_free(&token);
761
762    token.data = output_token.value;
763    token.length = output_token.length;
764
765    put32(c, 0); /* XXX fix gsm_error */
766    putdata(c, token);
767
768    gss_release_buffer(&min_stat, &output_token);
769
770    return 0;
771}
772
773
774static int
775HandleOP(Unwrap)
776{
777    OM_uint32 maj_stat, min_stat;
778    int32_t hContext, flags, seqno;
779    krb5_data token;
780    gss_ctx_id_t ctx;
781    gss_buffer_desc input_token, output_token;
782    int conf_state;
783    gss_qop_t qop_state;
784
785    ret32(c, hContext);
786    ret32(c, flags);
787    ret32(c, seqno);
788    retdata(c, token);
789
790    ctx = find_handle(c->handles, hContext, handle_context);
791    if (ctx == NULL)
792	errx(1, "unwrap: reference to unknown context");
793
794    input_token.length = token.length;
795    input_token.value = token.data;
796
797    maj_stat = gss_unwrap(&min_stat, ctx, &input_token,
798			  &output_token, &conf_state, &qop_state);
799
800    if (maj_stat != GSS_S_COMPLETE)
801	errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
802
803    krb5_data_free(&token);
804    if (maj_stat == GSS_S_COMPLETE) {
805	token.data = output_token.value;
806	token.length = output_token.length;
807    } else {
808	token.data = NULL;
809	token.length = 0;
810    }
811    put32(c, 0); /* XXX fix gsm_error */
812    putdata(c, token);
813
814    if (maj_stat == GSS_S_COMPLETE)
815	gss_release_buffer(&min_stat, &output_token);
816
817    return 0;
818}
819
820static int
821HandleOP(Encrypt)
822{
823    return handleWrap(op, c);
824}
825
826static int
827HandleOP(Decrypt)
828{
829    return handleUnwrap(op, c);
830}
831
832static int
833HandleOP(ConnectLoggingService2)
834{
835    errx(1, "ConnectLoggingService2");
836}
837
838static int
839HandleOP(GetMoniker)
840{
841    putstring(c, c->moniker);
842    return 0;
843}
844
845static int
846HandleOP(CallExtension)
847{
848    errx(1, "CallExtension");
849}
850
851static int
852HandleOP(AcquirePKInitCreds)
853{
854    int32_t flags;
855    krb5_data pfxdata;
856    char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX";
857    krb5_principal principal = NULL;
858    int fd;
859
860    ret32(c, flags);
861    retdata(c, pfxdata);
862
863    fd = mkstemp(fn + 5);
864    if (fd < 0)
865	errx(1, "mkstemp");
866
867    net_write(fd, pfxdata.data, pfxdata.length);
868    krb5_data_free(&pfxdata);
869    close(fd);
870
871    if (principal)
872	krb5_free_principal(context, principal);
873
874    put32(c, -1); /* hResource */
875    put32(c, GSMERR_NOT_SUPPORTED);
876    return 0;
877}
878
879static int
880HandleOP(WrapExt)
881{
882    OM_uint32 maj_stat, min_stat;
883    int32_t hContext, flags, bflags;
884    krb5_data token, header, trailer;
885    gss_ctx_id_t ctx;
886    unsigned char *p;
887    int conf_state, iov_len;
888    gss_iov_buffer_desc iov[6];
889
890    ret32(c, hContext);
891    ret32(c, flags);
892    ret32(c, bflags);
893    retdata(c, header);
894    retdata(c, token);
895    retdata(c, trailer);
896
897    ctx = find_handle(c->handles, hContext, handle_context);
898    if (ctx == NULL)
899	errx(1, "wrap: reference to unknown context");
900
901    memset(&iov, 0, sizeof(iov));
902
903    iov_len = sizeof(iov)/sizeof(iov[0]);
904
905    if (bflags & WRAP_EXP_ONLY_HEADER)
906	iov_len -= 2; /* skip trailer and padding, aka dce-style */
907
908    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
909    if (header.length != 0) {
910	iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
911	iov[1].buffer.length = header.length;
912	iov[1].buffer.value = header.data;
913    } else {
914	iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
915    }
916    iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
917    iov[2].buffer.length = token.length;
918    iov[2].buffer.value = token.data;
919    if (trailer.length != 0) {
920	iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
921	iov[3].buffer.length = trailer.length;
922	iov[3].buffer.value = trailer.data;
923    } else {
924	iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
925    }
926    iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
927    iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
928
929    maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state,
930				   iov, iov_len);
931    if (maj_stat != GSS_S_COMPLETE)
932	errx(1, "gss_wrap_iov_length failed");
933
934    maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state,
935			    iov, iov_len);
936    if (maj_stat != GSS_S_COMPLETE)
937	errx(1, "gss_wrap_iov failed");
938
939    krb5_data_free(&token);
940
941    token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length;
942    token.data = malloc(token.length);
943
944    p = token.data;
945    memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
946    p += iov[0].buffer.length;
947    memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
948    p += iov[2].buffer.length;
949    memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
950    p += iov[4].buffer.length;
951    memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
952    p += iov[5].buffer.length;
953
954    gss_release_iov_buffer(NULL, iov, iov_len);
955
956    put32(c, 0); /* XXX fix gsm_error */
957    putdata(c, token);
958
959    free(token.data);
960
961    return 0;
962}
963
964
965static int
966HandleOP(UnwrapExt)
967{
968    OM_uint32 maj_stat, min_stat;
969    int32_t hContext, flags, bflags;
970    krb5_data token, header, trailer;
971    gss_ctx_id_t ctx;
972    gss_iov_buffer_desc iov[3];
973    int conf_state, iov_len;
974    gss_qop_t qop_state;
975
976    ret32(c, hContext);
977    ret32(c, flags);
978    ret32(c, bflags);
979    retdata(c, header);
980    retdata(c, token);
981    retdata(c, trailer);
982
983    iov_len = sizeof(iov)/sizeof(iov[0]);
984
985    if (bflags & WRAP_EXP_ONLY_HEADER)
986	iov_len -= 1; /* skip trailer and padding, aka dce-style */
987
988    ctx = find_handle(c->handles, hContext, handle_context);
989    if (ctx == NULL)
990	errx(1, "unwrap: reference to unknown context");
991
992    if (header.length != 0) {
993	iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
994	iov[0].buffer.length = header.length;
995	iov[0].buffer.value = header.data;
996    } else {
997	iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY;
998    }
999    iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
1000    iov[1].buffer.length = token.length;
1001    iov[1].buffer.value = token.data;
1002
1003    if (trailer.length != 0) {
1004	iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
1005	iov[2].buffer.length = trailer.length;
1006	iov[2].buffer.value = trailer.data;
1007    } else {
1008	iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1009    }
1010
1011    maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state,
1012			      iov, iov_len);
1013
1014    if (maj_stat != GSS_S_COMPLETE)
1015	errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
1016
1017    if (maj_stat == GSS_S_COMPLETE) {
1018	token.data = iov[1].buffer.value;
1019	token.length = iov[1].buffer.length;
1020    } else {
1021	token.data = NULL;
1022	token.length = 0;
1023    }
1024    put32(c, 0); /* XXX fix gsm_error */
1025    putdata(c, token);
1026
1027    return 0;
1028}
1029
1030/*
1031 *
1032 */
1033
1034struct handler {
1035    enum gssMaggotOp op;
1036    const char *name;
1037    int (*func)(enum gssMaggotOp, struct client *);
1038};
1039
1040#define S(a) { e##a, #a, handle##a }
1041
1042struct handler handlers[] = {
1043    S(GetVersionInfo),
1044    S(GoodBye),
1045    S(InitContext),
1046    S(AcceptContext),
1047    S(ToastResource),
1048    S(AcquireCreds),
1049    S(Encrypt),
1050    S(Decrypt),
1051    S(Sign),
1052    S(Verify),
1053    S(GetVersionAndCapabilities),
1054    S(GetTargetName),
1055    S(SetLoggingSocket),
1056    S(ChangePassword),
1057    S(SetPasswordSelf),
1058    S(Wrap),
1059    S(Unwrap),
1060    S(ConnectLoggingService2),
1061    S(GetMoniker),
1062    S(CallExtension),
1063    S(AcquirePKInitCreds),
1064    S(WrapExt),
1065    S(UnwrapExt),
1066};
1067
1068#undef S
1069
1070/*
1071 *
1072 */
1073
1074static struct handler *
1075find_op(int32_t op)
1076{
1077    int i;
1078
1079    for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
1080	if (handlers[i].op == op)
1081	    return &handlers[i];
1082    return NULL;
1083}
1084
1085static struct client *
1086create_client(int fd, int port, const char *moniker)
1087{
1088    struct client *c;
1089
1090    c = ecalloc(1, sizeof(*c));
1091
1092    if (moniker) {
1093	c->moniker = estrdup(moniker);
1094    } else {
1095	char hostname[MAXHOSTNAMELEN];
1096	gethostname(hostname, sizeof(hostname));
1097	asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
1098    }
1099
1100    {
1101	c->salen = sizeof(c->sa);
1102	getpeername(fd, (struct sockaddr *)&c->sa, &c->salen);
1103
1104	getnameinfo((struct sockaddr *)&c->sa, c->salen,
1105		    c->servername, sizeof(c->servername),
1106		    NULL, 0, NI_NUMERICHOST);
1107    }
1108
1109    c->sock = krb5_storage_from_fd(fd);
1110    if (c->sock == NULL)
1111	errx(1, "krb5_storage_from_fd");
1112
1113    close(fd);
1114
1115    return c;
1116}
1117
1118static void
1119free_client(struct client *c)
1120{
1121    while(c->handles)
1122	del_handle(&c->handles, c->handles->idx);
1123
1124    free(c->moniker);
1125    krb5_storage_free(c->sock);
1126    if (c->logging)
1127	krb5_storage_free(c->logging);
1128    free(c);
1129}
1130
1131
1132static void *
1133handleServer(void *ptr)
1134{
1135    struct handler *handler;
1136    struct client *c;
1137    int32_t op;
1138
1139    c = (struct client *)ptr;
1140
1141
1142    while(1) {
1143	ret32(c, op);
1144
1145	handler = find_op(op);
1146	if (handler == NULL) {
1147	    logmessage(c, __FILE__, __LINE__, 0,
1148		       "op %d not supported", (int)op);
1149	    exit(1);
1150	}
1151
1152	logmessage(c, __FILE__, __LINE__, 0,
1153		   "---> Got op %s from server %s",
1154		   handler->name, c->servername);
1155
1156	if ((handler->func)(handler->op, c))
1157	    break;
1158    }
1159
1160    return NULL;
1161}
1162
1163
1164static char *port_str;
1165static int version_flag;
1166static int help_flag;
1167static char *logfile_str;
1168static char *moniker_str;
1169
1170static int port = 4711;
1171
1172struct getargs args[] = {
1173    { "spn",	0,   arg_string,	&targetname,	"This host's SPN",
1174      "service/host@REALM" },
1175    { "port",	'p', arg_string,	&port_str,	"Use this port",
1176      "number-of-service" },
1177    { "logfile", 0,  arg_string,	&logfile_str,	"logfile",
1178      "number-of-service" },
1179    { "moniker", 0,  arg_string,	&moniker_str,	"nickname",
1180      "name" },
1181    { "version", 0,  arg_flag,		&version_flag,	"Print version",
1182      NULL },
1183    { "help",	 0,  arg_flag,		&help_flag,	NULL,
1184      NULL }
1185};
1186
1187static void
1188usage(int ret)
1189{
1190    arg_printusage (args,
1191		    sizeof(args) / sizeof(args[0]),
1192		    NULL,
1193		    "");
1194    exit (ret);
1195}
1196
1197int
1198main(int argc, char **argv)
1199{
1200    int optidx	= 0;
1201
1202    setprogname (argv[0]);
1203
1204    if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
1205	usage (1);
1206
1207    if (help_flag)
1208	usage (0);
1209
1210    if (version_flag) {
1211	print_version (NULL);
1212	return 0;
1213    }
1214
1215    if (optidx != argc)
1216	usage (1);
1217
1218    if (port_str) {
1219	char *ptr;
1220
1221	port = strtol (port_str, &ptr, 10);
1222	if (port == 0 && ptr == port_str)
1223	    errx (1, "Bad port `%s'", port_str);
1224    }
1225
1226    krb5_init_context(&context);
1227
1228    {
1229	const char *lf = logfile_str;
1230	if (lf == NULL)
1231	    lf = "/dev/tty";
1232
1233	logfile = fopen(lf, "w");
1234	if (logfile == NULL)
1235	    err(1, "error opening %s", lf);
1236    }
1237
1238    mini_inetd(htons(port), NULL);
1239    fprintf(logfile, "connected\n");
1240
1241    {
1242	struct client *c;
1243
1244	c = create_client(0, port, moniker_str);
1245	/* close(0); */
1246
1247	handleServer(c);
1248
1249	free_client(c);
1250    }
1251
1252    krb5_free_context(context);
1253
1254    return 0;
1255}
1256