xref: /freebsd/crypto/heimdal/appl/gssmask/gssmask.c (revision 7877fdebeeb35fad1cbbafce22598b1bdf97c786)
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"
35 RCSID("$Id$");
36 
37 /*
38  *
39  */
40 
41 enum handle_type { handle_context, handle_cred };
42 
43 struct handle {
44     int32_t idx;
45     enum handle_type type;
46     void *ptr;
47     struct handle *next;
48 };
49 
50 struct 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 
61 FILE *logfile;
62 static char *targetname;
63 krb5_context context;
64 
65 /*
66  *
67  */
68 
69 static void
70 logmessage(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 
103 static int32_t
104 add_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 
119 static void
120 del_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 
149 static void *
150 find_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 
167 static int32_t
168 convert_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 
184 static int32_t
185 convert_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 
199 static int32_t
200 acquire_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) \
270 handle##h(enum gssMaggotOp op, struct client *c)
271 
272 /*
273  *
274  */
275 
276 static int
277 HandleOP(GetVersionInfo)
278 {
279     put32(c, GSSMAGGOTPROTOCOL);
280     errx(1, "GetVersionInfo");
281 }
282 
283 static int
284 HandleOP(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 
300 static int
301 HandleOP(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 
401 out:
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 
418 static int
419 HandleOP(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 
503 static int
504 HandleOP(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 
516 static int
517 HandleOP(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 
546 out:
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 
563 static int
564 HandleOP(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 
602 static int
603 HandleOP(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 
643 static int
644 HandleOP(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 
672 static int
673 HandleOP(GetTargetName)
674 {
675     if (targetname)
676 	putstring(c, targetname);
677     else
678 	putstring(c, "");
679     return 0;
680 }
681 
682 static int
683 HandleOP(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 
721 static int
722 HandleOP(ChangePassword)
723 {
724     errx(1, "ChangePassword");
725 }
726 
727 static int
728 HandleOP(SetPasswordSelf)
729 {
730     errx(1, "SetPasswordSelf");
731 }
732 
733 static int
734 HandleOP(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 
774 static int
775 HandleOP(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 
820 static int
821 HandleOP(Encrypt)
822 {
823     return handleWrap(op, c);
824 }
825 
826 static int
827 HandleOP(Decrypt)
828 {
829     return handleUnwrap(op, c);
830 }
831 
832 static int
833 HandleOP(ConnectLoggingService2)
834 {
835     errx(1, "ConnectLoggingService2");
836 }
837 
838 static int
839 HandleOP(GetMoniker)
840 {
841     putstring(c, c->moniker);
842     return 0;
843 }
844 
845 static int
846 HandleOP(CallExtension)
847 {
848     errx(1, "CallExtension");
849 }
850 
851 static int
852 HandleOP(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 
879 static int
880 HandleOP(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 
965 static int
966 HandleOP(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 
1034 struct 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 
1042 struct 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 
1074 static struct handler *
1075 find_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 
1085 static struct client *
1086 create_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 
1118 static void
1119 free_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 
1132 static void *
1133 handleServer(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 
1164 static char *port_str;
1165 static int version_flag;
1166 static int help_flag;
1167 static char *logfile_str;
1168 static char *moniker_str;
1169 
1170 static int port = 4711;
1171 
1172 struct 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 
1187 static void
1188 usage(int ret)
1189 {
1190     arg_printusage (args,
1191 		    sizeof(args) / sizeof(args[0]),
1192 		    NULL,
1193 		    "");
1194     exit (ret);
1195 }
1196 
1197 int
1198 main(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