]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/appl/gssmask/gssmask.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / appl / gssmask / gssmask.c
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 }