gss: update SAnon for draft-howard-gss-sanon-13
[heimdal.git] / appl / gssmask / gssmask.c
blob35c548979a6f287316c3838fcf05acb9a6cc88eb
1 /*
2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "common.h"
35 RCSID("$Id$");
41 enum handle_type { handle_context, handle_cred };
43 struct handle {
44 int32_t idx;
45 enum handle_type type;
46 void *ptr;
47 struct handle *next;
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];
61 FILE *logfile;
62 static char *targetname;
63 krb5_context context;
69 static void
70 logmessage(struct client *c, const char *file, unsigned int lineno,
71 int level, const char *fmt, ...)
73 char *message;
74 va_list ap;
75 int32_t ackid;
76 int ret;
78 va_start(ap, fmt);
79 ret = vasprintf(&message, fmt, ap);
80 va_end(ap);
81 if (ret == -1)
82 errx(1, "out of memory");
84 if (logfile)
85 fprintf(logfile, "%s:%u: %d %s\n", file, lineno, level, message);
87 if (c->logging) {
88 if (krb5_store_int32(c->logging, eLogInfo) != 0)
89 errx(1, "krb5_store_int32: log level");
90 if (krb5_store_string(c->logging, file) != 0)
91 errx(1, "krb5_store_string: filename");
92 if (krb5_store_int32(c->logging, lineno) != 0)
93 errx(1, "krb5_store_string: filename");
94 if (krb5_store_string(c->logging, message) != 0)
95 errx(1, "krb5_store_string: message");
96 if (krb5_ret_int32(c->logging, &ackid) != 0)
97 errx(1, "krb5_ret_int32: ackid");
99 free(message);
106 static int32_t
107 add_handle(struct client *c, enum handle_type type, void *data)
109 struct handle *h;
111 h = ecalloc(1, sizeof(*h));
113 h->idx = ++c->nHandle;
114 h->type = type;
115 h->ptr = data;
116 h->next = c->handles;
117 c->handles = h;
119 return h->idx;
122 static void
123 del_handle(struct handle **h, int32_t idx)
125 OM_uint32 min_stat;
127 if (idx == 0)
128 return;
130 while (*h) {
131 if ((*h)->idx == idx) {
132 struct handle *p = *h;
133 *h = (*h)->next;
134 switch(p->type) {
135 case handle_context: {
136 gss_ctx_id_t c = p->ptr;
137 gss_delete_sec_context(&min_stat, &c, NULL);
138 break; }
139 case handle_cred: {
140 gss_cred_id_t c = p->ptr;
141 gss_release_cred(&min_stat, &c);
142 break; }
144 free(p);
145 return;
147 h = &((*h)->next);
149 errx(1, "tried to delete an unexisting handle");
152 static void *
153 find_handle(struct handle *h, int32_t idx, enum handle_type type)
155 if (idx == 0)
156 return NULL;
158 while (h) {
159 if (h->idx == idx) {
160 if (type == h->type)
161 return h->ptr;
162 errx(1, "monger switched type on handle!");
164 h = h->next;
166 return NULL;
170 static int32_t
171 convert_gss_to_gsm(OM_uint32 maj_stat)
173 switch(maj_stat) {
174 case 0:
175 return GSMERR_OK;
176 case GSS_S_CONTINUE_NEEDED:
177 return GSMERR_CONTINUE_NEEDED;
178 case GSS_S_DEFECTIVE_TOKEN:
179 return GSMERR_INVALID_TOKEN;
180 case GSS_S_BAD_MIC:
181 return GSMERR_AP_MODIFIED;
182 default:
183 return GSMERR_ERROR;
187 static int32_t
188 convert_krb5_to_gsm(krb5_error_code ret)
190 switch(ret) {
191 case 0:
192 return GSMERR_OK;
193 default:
194 return GSMERR_ERROR;
202 static int32_t
203 acquire_cred(struct client *c,
204 krb5_principal principal,
205 krb5_get_init_creds_opt *opt,
206 int32_t *handle)
208 krb5_error_code ret;
209 krb5_creds cred;
210 krb5_ccache id;
211 gss_cred_id_t gcred;
212 OM_uint32 maj_stat, min_stat;
214 *handle = 0;
216 krb5_get_init_creds_opt_set_forwardable (opt, 1);
217 krb5_get_init_creds_opt_set_renew_life (opt, 3600 * 24 * 30);
219 memset(&cred, 0, sizeof(cred));
221 ret = krb5_get_init_creds_password (context,
222 &cred,
223 principal,
224 NULL,
225 NULL,
226 NULL,
228 NULL,
229 opt);
230 if (ret) {
231 logmessage(c, __FILE__, __LINE__, 0,
232 "krb5_get_init_creds failed: %d", ret);
233 return convert_krb5_to_gsm(ret);
236 ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);
237 if (ret)
238 krb5_err (context, 1, ret, "krb5_cc_initialize");
240 ret = krb5_cc_initialize (context, id, cred.client);
241 if (ret)
242 krb5_err (context, 1, ret, "krb5_cc_initialize");
244 ret = krb5_cc_store_cred (context, id, &cred);
245 if (ret)
246 krb5_err (context, 1, ret, "krb5_cc_store_cred");
248 krb5_free_cred_contents (context, &cred);
250 maj_stat = gss_krb5_import_cred(&min_stat,
252 NULL,
253 NULL,
254 &gcred);
255 krb5_cc_close(context, id);
256 if (maj_stat) {
257 logmessage(c, __FILE__, __LINE__, 0,
258 "krb5 import creds failed with: %d", maj_stat);
259 return convert_gss_to_gsm(maj_stat);
262 *handle = add_handle(c, handle_cred, gcred);
264 return 0;
272 #define HandleOP(h) \
273 handle##h(enum gssMaggotOp op, struct client *c)
279 static int
280 HandleOP(GetVersionInfo)
282 put32(c, GSSMAGGOTPROTOCOL);
283 errx(1, "GetVersionInfo");
286 static int
287 HandleOP(GoodBye)
289 struct handle *h = c->handles;
290 unsigned int i = 0;
292 while (h) {
293 h = h->next;
294 i++;
297 if (i)
298 logmessage(c, __FILE__, __LINE__, 0,
299 "Did not toast all resources: %d", i);
300 return 1;
303 static int
304 HandleOP(InitContext)
306 OM_uint32 maj_stat, min_stat, ret_flags;
307 int32_t hContext, hCred, flags;
308 krb5_data target_name, in_token;
309 int32_t new_context_id = 0, gsm_error = 0;
310 krb5_data out_token = { 0 , NULL };
312 gss_ctx_id_t ctx;
313 gss_cred_id_t creds;
314 gss_name_t gss_target_name;
315 gss_buffer_desc input_token;
316 gss_buffer_desc output_token = {0, 0};
317 gss_OID oid = GSS_C_NO_OID;
318 gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
320 ret32(c, hContext);
321 ret32(c, hCred);
322 ret32(c, flags);
323 retdata(c, target_name);
324 retdata(c, in_token);
326 logmessage(c, __FILE__, __LINE__, 0,
327 "targetname: <%.*s>", (int)target_name.length,
328 (char *)target_name.data);
330 ctx = find_handle(c->handles, hContext, handle_context);
331 if (ctx == NULL)
332 hContext = 0;
333 creds = find_handle(c->handles, hCred, handle_cred);
334 if (creds == NULL)
335 abort();
337 input_token.length = target_name.length;
338 input_token.value = target_name.data;
340 maj_stat = gss_import_name(&min_stat,
341 &input_token,
342 GSS_KRB5_NT_PRINCIPAL_NAME,
343 &gss_target_name);
344 if (GSS_ERROR(maj_stat)) {
345 logmessage(c, __FILE__, __LINE__, 0,
346 "import name creds failed with: %d", maj_stat);
347 gsm_error = convert_gss_to_gsm(maj_stat);
348 goto out;
351 /* oid from flags */
353 if (in_token.length) {
354 input_token.length = in_token.length;
355 input_token.value = in_token.data;
356 input_token_ptr = &input_token;
357 if (ctx == NULL)
358 krb5_errx(context, 1, "initcreds, context NULL, but not first req");
359 } else {
360 input_token.length = 0;
361 input_token.value = NULL;
362 if (ctx)
363 krb5_errx(context, 1, "initcreds, context not NULL, but first req");
366 if ((flags & GSS_C_DELEG_FLAG) != 0)
367 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating");
368 if ((flags & GSS_C_DCE_STYLE) != 0)
369 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style");
371 maj_stat = gss_init_sec_context(&min_stat,
372 creds,
373 &ctx,
374 gss_target_name,
375 oid,
376 flags & 0x7f,
378 NULL,
379 input_token_ptr,
380 NULL,
381 &output_token,
382 &ret_flags,
383 NULL);
384 if (GSS_ERROR(maj_stat)) {
385 if (hContext != 0)
386 del_handle(&c->handles, hContext);
387 new_context_id = 0;
388 logmessage(c, __FILE__, __LINE__, 0,
389 "gss_init_sec_context returns code: %d/%d",
390 maj_stat, min_stat);
391 } else {
392 if (input_token.length == 0)
393 new_context_id = add_handle(c, handle_context, ctx);
394 else
395 new_context_id = hContext;
398 gsm_error = convert_gss_to_gsm(maj_stat);
400 if (output_token.length) {
401 out_token.data = output_token.value;
402 out_token.length = output_token.length;
405 out:
406 logmessage(c, __FILE__, __LINE__, 0,
407 "InitContext return code: %d", gsm_error);
409 put32(c, new_context_id);
410 put32(c, gsm_error);
411 putdata(c, out_token);
413 gss_release_name(&min_stat, &gss_target_name);
414 if (output_token.length)
415 gss_release_buffer(&min_stat, &output_token);
416 krb5_data_free(&in_token);
417 krb5_data_free(&target_name);
419 return 0;
422 static int
423 HandleOP(AcceptContext)
425 OM_uint32 maj_stat, min_stat, ret_flags;
426 int32_t hContext, deleg_hcred, flags;
427 krb5_data in_token;
428 int32_t new_context_id = 0, gsm_error = 0;
429 krb5_data out_token = { 0 , NULL };
431 gss_ctx_id_t ctx;
432 gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL;
433 gss_buffer_desc input_token, output_token;
435 ret32(c, hContext);
436 ret32(c, flags);
437 retdata(c, in_token);
439 ctx = find_handle(c->handles, hContext, handle_context);
440 if (ctx == NULL)
441 hContext = 0;
443 if (in_token.length) {
444 input_token.length = in_token.length;
445 input_token.value = in_token.data;
446 } else {
447 input_token.length = 0;
448 input_token.value = NULL;
451 maj_stat = gss_accept_sec_context(&min_stat,
452 &ctx,
453 GSS_C_NO_CREDENTIAL,
454 &input_token,
455 GSS_C_NO_CHANNEL_BINDINGS,
456 NULL,
457 NULL,
458 &output_token,
459 &ret_flags,
460 NULL,
461 &deleg_cred);
462 if (GSS_ERROR(maj_stat)) {
463 if (hContext != 0)
464 del_handle(&c->handles, hContext);
465 logmessage(c, __FILE__, __LINE__, 0,
466 "gss_accept_sec_context returns code: %d/%d",
467 maj_stat, min_stat);
468 new_context_id = 0;
469 } else {
470 if (hContext == 0)
471 new_context_id = add_handle(c, handle_context, ctx);
472 else
473 new_context_id = hContext;
475 if (output_token.length) {
476 out_token.data = output_token.value;
477 out_token.length = output_token.length;
479 if ((ret_flags & GSS_C_DCE_STYLE) != 0)
480 logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style");
481 if ((ret_flags & GSS_C_DELEG_FLAG) != 0) {
482 deleg_hcred = add_handle(c, handle_cred, deleg_cred);
483 logmessage(c, __FILE__, __LINE__, 0,
484 "accept_context delegated handle: %d", deleg_hcred);
485 } else {
486 gss_release_cred(&min_stat, &deleg_cred);
487 deleg_hcred = 0;
491 gsm_error = convert_gss_to_gsm(maj_stat);
493 put32(c, new_context_id);
494 put32(c, gsm_error);
495 putdata(c, out_token);
496 put32(c, deleg_hcred);
498 if (output_token.length)
499 gss_release_buffer(&min_stat, &output_token);
500 krb5_data_free(&in_token);
502 return 0;
505 static int
506 HandleOP(ToastResource)
508 int32_t handle;
510 ret32(c, handle);
511 logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle);
512 del_handle(&c->handles, handle);
513 put32(c, GSMERR_OK);
515 return 0;
518 static int
519 HandleOP(AcquireCreds)
521 char *name, *password;
522 int32_t gsm_error, flags, handle = 0;
523 krb5_principal principal = NULL;
524 krb5_get_init_creds_opt *opt = NULL;
525 krb5_error_code ret;
527 retstring(c, name);
528 retstring(c, password);
529 ret32(c, flags);
531 logmessage(c, __FILE__, __LINE__, 0,
532 "username: %s password: %s", name, password);
534 ret = krb5_parse_name(context, name, &principal);
535 if (ret) {
536 gsm_error = convert_krb5_to_gsm(ret);
537 goto out;
540 ret = krb5_get_init_creds_opt_alloc (context, &opt);
541 if (ret)
542 krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
544 krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL);
546 gsm_error = acquire_cred(c, principal, opt, &handle);
548 out:
549 logmessage(c, __FILE__, __LINE__, 0,
550 "AcquireCreds handle: %d return code: %d", handle, gsm_error);
552 if (opt)
553 krb5_get_init_creds_opt_free (context, opt);
554 if (principal)
555 krb5_free_principal(context, principal);
556 free(name);
557 free(password);
559 put32(c, gsm_error);
560 put32(c, handle);
562 return 0;
565 static int
566 HandleOP(Sign)
568 OM_uint32 maj_stat, min_stat;
569 int32_t hContext, flags, seqno;
570 krb5_data token;
571 gss_ctx_id_t ctx;
572 gss_buffer_desc input_token, output_token;
574 ret32(c, hContext);
575 ret32(c, flags);
576 ret32(c, seqno);
577 retdata(c, token);
579 ctx = find_handle(c->handles, hContext, handle_context);
580 if (ctx == NULL)
581 errx(1, "sign: reference to unknown context");
583 input_token.length = token.length;
584 input_token.value = token.data;
586 maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token,
587 &output_token);
588 if (maj_stat != GSS_S_COMPLETE)
589 errx(1, "gss_get_mic failed");
591 krb5_data_free(&token);
593 token.data = output_token.value;
594 token.length = output_token.length;
596 put32(c, 0); /* XXX fix gsm_error */
597 putdata(c, token);
599 gss_release_buffer(&min_stat, &output_token);
601 return 0;
604 static int
605 HandleOP(Verify)
607 OM_uint32 maj_stat, min_stat;
608 int32_t hContext, flags, seqno;
609 krb5_data msg, mic;
610 gss_ctx_id_t ctx;
611 gss_buffer_desc msg_token, mic_token;
612 gss_qop_t qop;
614 ret32(c, hContext);
616 ctx = find_handle(c->handles, hContext, handle_context);
617 if (ctx == NULL)
618 errx(1, "verify: reference to unknown context");
620 ret32(c, flags);
621 ret32(c, seqno);
622 retdata(c, msg);
624 msg_token.length = msg.length;
625 msg_token.value = msg.data;
627 retdata(c, mic);
629 mic_token.length = mic.length;
630 mic_token.value = mic.data;
632 maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token,
633 &mic_token, &qop);
634 if (maj_stat != GSS_S_COMPLETE)
635 errx(1, "gss_verify_mic failed");
637 krb5_data_free(&mic);
638 krb5_data_free(&msg);
640 put32(c, 0); /* XXX fix gsm_error */
642 return 0;
645 static int
646 HandleOP(GetVersionAndCapabilities)
648 int32_t cap = HAS_MONIKER;
649 char name[256] = "unknown", *str;
650 int ret;
652 if (targetname)
653 cap |= ISSERVER; /* is server */
655 #ifdef HAVE_UNAME
657 struct utsname ut;
658 if (uname(&ut) == 0) {
659 snprintf(name, sizeof(name), "%s-%s-%s",
660 ut.sysname, ut.version, ut.machine);
663 #endif
665 ret = asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name);
666 if (ret == -1)
667 errx(1, "out of memory");
669 put32(c, GSSMAGGOTPROTOCOL);
670 put32(c, cap);
671 putstring(c, str);
672 free(str);
674 return 0;
677 static int
678 HandleOP(GetTargetName)
680 if (targetname)
681 putstring(c, targetname);
682 else
683 putstring(c, "");
684 return 0;
687 static int
688 HandleOP(SetLoggingSocket)
690 int32_t portnum;
691 krb5_socket_t sock;
692 int ret;
694 ret32(c, portnum);
696 logmessage(c, __FILE__, __LINE__, 0,
697 "logging port on peer is: %d", (int)portnum);
699 socket_set_port((struct sockaddr *)(&c->sa), htons(portnum));
701 sock = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0);
702 if (sock == rk_INVALID_SOCKET)
703 return 0;
705 ret = connect(sock, (struct sockaddr *)&c->sa, c->salen);
706 if (ret < 0) {
707 logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s",
708 strerror(errno));
709 rk_closesocket(sock);
710 return 0;
713 if (c->logging)
714 krb5_storage_free(c->logging);
715 c->logging = krb5_storage_from_socket(sock);
716 rk_closesocket(sock);
718 krb5_store_int32(c->logging, eLogSetMoniker);
719 store_string(c->logging, c->moniker);
721 logmessage(c, __FILE__, __LINE__, 0, "logging turned on");
723 return 0;
727 static int
728 HandleOP(ChangePassword)
730 errx(1, "ChangePassword");
733 static int
734 HandleOP(SetPasswordSelf)
736 errx(1, "SetPasswordSelf");
739 static int
740 HandleOP(Wrap)
742 OM_uint32 maj_stat, min_stat;
743 int32_t hContext, flags, seqno;
744 krb5_data token;
745 gss_ctx_id_t ctx;
746 gss_buffer_desc input_token, output_token;
747 int conf_state;
749 ret32(c, hContext);
750 ret32(c, flags);
751 ret32(c, seqno);
752 retdata(c, token);
754 ctx = find_handle(c->handles, hContext, handle_context);
755 if (ctx == NULL)
756 errx(1, "wrap: reference to unknown context");
758 input_token.length = token.length;
759 input_token.value = token.data;
761 maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token,
762 &conf_state, &output_token);
763 if (maj_stat != GSS_S_COMPLETE)
764 errx(1, "gss_wrap failed");
766 krb5_data_free(&token);
768 token.data = output_token.value;
769 token.length = output_token.length;
771 put32(c, 0); /* XXX fix gsm_error */
772 putdata(c, token);
774 gss_release_buffer(&min_stat, &output_token);
776 return 0;
780 static int
781 HandleOP(Unwrap)
783 OM_uint32 maj_stat, min_stat;
784 int32_t hContext, flags, seqno;
785 krb5_data token;
786 gss_ctx_id_t ctx;
787 gss_buffer_desc input_token, output_token;
788 int conf_state;
789 gss_qop_t qop_state;
791 ret32(c, hContext);
792 ret32(c, flags);
793 ret32(c, seqno);
794 retdata(c, token);
796 ctx = find_handle(c->handles, hContext, handle_context);
797 if (ctx == NULL)
798 errx(1, "unwrap: reference to unknown context");
800 input_token.length = token.length;
801 input_token.value = token.data;
803 maj_stat = gss_unwrap(&min_stat, ctx, &input_token,
804 &output_token, &conf_state, &qop_state);
806 if (maj_stat != GSS_S_COMPLETE)
807 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
809 krb5_data_free(&token);
810 if (maj_stat == GSS_S_COMPLETE) {
811 token.data = output_token.value;
812 token.length = output_token.length;
813 } else {
814 token.data = NULL;
815 token.length = 0;
817 put32(c, 0); /* XXX fix gsm_error */
818 putdata(c, token);
820 if (maj_stat == GSS_S_COMPLETE)
821 gss_release_buffer(&min_stat, &output_token);
823 return 0;
826 static int
827 HandleOP(Encrypt)
829 return handleWrap(op, c);
832 static int
833 HandleOP(Decrypt)
835 return handleUnwrap(op, c);
838 static int
839 HandleOP(ConnectLoggingService2)
841 errx(1, "ConnectLoggingService2");
844 static int
845 HandleOP(GetMoniker)
847 putstring(c, c->moniker);
848 return 0;
851 static int
852 HandleOP(CallExtension)
854 errx(1, "CallExtension");
857 static int
858 HandleOP(AcquirePKInitCreds)
860 int32_t flags;
861 krb5_data pfxdata;
862 char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX";
863 int fd;
865 ret32(c, flags);
866 retdata(c, pfxdata);
868 fd = mkstemp(fn + 5);
869 if (fd < 0)
870 errx(1, "mkstemp");
872 net_write(fd, pfxdata.data, pfxdata.length);
873 krb5_data_free(&pfxdata);
874 close(fd);
876 put32(c, -1); /* hResource */
877 put32(c, GSMERR_NOT_SUPPORTED);
878 return 0;
881 static int
882 HandleOP(WrapExt)
884 OM_uint32 maj_stat, min_stat;
885 int32_t hContext, flags, bflags;
886 krb5_data token, header, trailer;
887 gss_ctx_id_t ctx;
888 unsigned char *p;
889 int conf_state, iov_len;
890 gss_iov_buffer_desc iov[6];
892 ret32(c, hContext);
893 ret32(c, flags);
894 ret32(c, bflags);
895 retdata(c, header);
896 retdata(c, token);
897 retdata(c, trailer);
899 ctx = find_handle(c->handles, hContext, handle_context);
900 if (ctx == NULL)
901 errx(1, "wrap: reference to unknown context");
903 memset(&iov, 0, sizeof(iov));
905 iov_len = sizeof(iov)/sizeof(iov[0]);
907 if (bflags & WRAP_EXP_ONLY_HEADER)
908 iov_len -= 2; /* skip trailer and padding, aka dce-style */
910 iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
911 if (header.length != 0) {
912 iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
913 iov[1].buffer.length = header.length;
914 iov[1].buffer.value = header.data;
915 } else {
916 iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
918 iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
919 iov[2].buffer.length = token.length;
920 iov[2].buffer.value = token.data;
921 if (trailer.length != 0) {
922 iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
923 iov[3].buffer.length = trailer.length;
924 iov[3].buffer.value = trailer.data;
925 } else {
926 iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
928 iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
929 iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
931 maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state,
932 iov, iov_len);
933 if (maj_stat != GSS_S_COMPLETE)
934 errx(1, "gss_wrap_iov_length failed");
936 maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state,
937 iov, iov_len);
938 if (maj_stat != GSS_S_COMPLETE)
939 errx(1, "gss_wrap_iov failed");
941 krb5_data_free(&token);
943 token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length;
944 token.data = malloc(token.length);
946 p = token.data;
947 memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
948 p += iov[0].buffer.length;
949 memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
950 p += iov[2].buffer.length;
951 memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
952 p += iov[4].buffer.length;
953 memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
954 p += iov[5].buffer.length;
956 gss_release_iov_buffer(NULL, iov, iov_len);
958 put32(c, 0); /* XXX fix gsm_error */
959 putdata(c, token);
961 free(token.data);
963 return 0;
967 static int
968 HandleOP(UnwrapExt)
970 OM_uint32 maj_stat, min_stat;
971 int32_t hContext, flags, bflags;
972 krb5_data token, header, trailer;
973 gss_ctx_id_t ctx;
974 gss_iov_buffer_desc iov[3];
975 int conf_state, iov_len;
976 gss_qop_t qop_state;
978 ret32(c, hContext);
979 ret32(c, flags);
980 ret32(c, bflags);
981 retdata(c, header);
982 retdata(c, token);
983 retdata(c, trailer);
985 iov_len = sizeof(iov)/sizeof(iov[0]);
987 if (bflags & WRAP_EXP_ONLY_HEADER)
988 iov_len -= 1; /* skip trailer and padding, aka dce-style */
990 ctx = find_handle(c->handles, hContext, handle_context);
991 if (ctx == NULL)
992 errx(1, "unwrap: reference to unknown context");
994 if (header.length != 0) {
995 iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
996 iov[0].buffer.length = header.length;
997 iov[0].buffer.value = header.data;
998 } else {
999 iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1001 iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
1002 iov[1].buffer.length = token.length;
1003 iov[1].buffer.value = token.data;
1005 if (trailer.length != 0) {
1006 iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
1007 iov[2].buffer.length = trailer.length;
1008 iov[2].buffer.value = trailer.data;
1009 } else {
1010 iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1013 maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state,
1014 iov, iov_len);
1016 if (maj_stat != GSS_S_COMPLETE)
1017 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
1019 if (maj_stat == GSS_S_COMPLETE) {
1020 token.data = iov[1].buffer.value;
1021 token.length = iov[1].buffer.length;
1022 } else {
1023 token.data = NULL;
1024 token.length = 0;
1026 put32(c, 0); /* XXX fix gsm_error */
1027 putdata(c, token);
1029 return 0;
1036 struct handler {
1037 enum gssMaggotOp op;
1038 const char *name;
1039 int (*func)(enum gssMaggotOp, struct client *);
1042 #define S(a) { e##a, #a, handle##a }
1044 struct handler handlers[] = {
1045 S(GetVersionInfo),
1046 S(GoodBye),
1047 S(InitContext),
1048 S(AcceptContext),
1049 S(ToastResource),
1050 S(AcquireCreds),
1051 S(Encrypt),
1052 S(Decrypt),
1053 S(Sign),
1054 S(Verify),
1055 S(GetVersionAndCapabilities),
1056 S(GetTargetName),
1057 S(SetLoggingSocket),
1058 S(ChangePassword),
1059 S(SetPasswordSelf),
1060 S(Wrap),
1061 S(Unwrap),
1062 S(ConnectLoggingService2),
1063 S(GetMoniker),
1064 S(CallExtension),
1065 S(AcquirePKInitCreds),
1066 S(WrapExt),
1067 S(UnwrapExt),
1070 #undef S
1076 static struct handler *
1077 find_op(int32_t op)
1079 int i;
1081 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
1082 if (handlers[i].op == op)
1083 return &handlers[i];
1084 return NULL;
1087 static struct client *
1088 create_client(krb5_socket_t sock, int port, const char *moniker)
1090 struct client *c;
1091 int ret;
1093 c = ecalloc(1, sizeof(*c));
1095 if (moniker) {
1096 c->moniker = estrdup(moniker);
1097 } else {
1098 char hostname[MAXHOSTNAMELEN];
1099 gethostname(hostname, sizeof(hostname));
1100 ret = asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
1101 if (ret == -1)
1102 c->moniker = NULL;
1105 if (!c->moniker)
1106 errx(1, "out of memory");
1109 c->salen = sizeof(c->sa);
1110 getpeername(sock, (struct sockaddr *)&c->sa, &c->salen);
1112 getnameinfo((struct sockaddr *)&c->sa, c->salen,
1113 c->servername, sizeof(c->servername),
1114 NULL, 0, NI_NUMERICHOST);
1117 c->sock = krb5_storage_from_socket(sock);
1118 if (c->sock == NULL)
1119 errx(1, "krb5_storage_from_socket");
1121 rk_closesocket(sock);
1123 return c;
1126 static void
1127 free_client(struct client *c)
1129 while(c->handles)
1130 del_handle(&c->handles, c->handles->idx);
1132 free(c->moniker);
1133 krb5_storage_free(c->sock);
1134 if (c->logging)
1135 krb5_storage_free(c->logging);
1136 free(c);
1140 static void *
1141 handleServer(void *ptr)
1143 struct handler *handler;
1144 struct client *c;
1145 int32_t op;
1147 c = (struct client *)ptr;
1150 while(1) {
1151 ret32(c, op);
1153 handler = find_op(op);
1154 if (handler == NULL) {
1155 logmessage(c, __FILE__, __LINE__, 0,
1156 "op %d not supported", (int)op);
1157 exit(1);
1160 logmessage(c, __FILE__, __LINE__, 0,
1161 "---> Got op %s from server %s",
1162 handler->name, c->servername);
1164 if ((handler->func)(handler->op, c))
1165 break;
1168 return NULL;
1172 static char *port_str;
1173 static int version_flag;
1174 static int help_flag;
1175 static char *logfile_str;
1176 static char *moniker_str;
1178 static int port = 4711;
1180 struct getargs args[] = {
1181 { "spn", 0, arg_string, &targetname, "This host's SPN",
1182 "service/host@REALM" },
1183 { "port", 'p', arg_string, &port_str, "Use this port",
1184 "number-of-service" },
1185 { "logfile", 0, arg_string, &logfile_str, "logfile",
1186 "number-of-service" },
1187 { "moniker", 0, arg_string, &moniker_str, "nickname",
1188 "name" },
1189 { "version", 0, arg_flag, &version_flag, "Print version",
1190 NULL },
1191 { "help", 0, arg_flag, &help_flag, NULL,
1192 NULL }
1195 static void
1196 usage(int ret)
1198 arg_printusage (args,
1199 sizeof(args) / sizeof(args[0]),
1200 NULL,
1201 "");
1202 exit (ret);
1206 main(int argc, char **argv)
1208 int optidx = 0;
1209 krb5_error_code ret;
1211 setprogname (argv[0]);
1213 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
1214 usage (1);
1216 if (help_flag)
1217 usage (0);
1219 if (version_flag) {
1220 print_version (NULL);
1221 return 0;
1224 if (optidx != argc)
1225 usage (1);
1227 if (port_str) {
1228 char *ptr;
1230 port = strtol (port_str, &ptr, 10);
1231 if (port == 0 && ptr == port_str)
1232 errx (1, "Bad port `%s'", port_str);
1235 ret = krb5_init_context(&context);
1236 if (ret)
1237 errx(1, "Error initializing kerberos: %d", ret);
1240 const char *lf = logfile_str;
1241 if (lf == NULL)
1242 lf = "/dev/tty";
1244 logfile = fopen(lf, "w");
1245 if (logfile == NULL)
1246 err(1, "error opening %s", lf);
1249 mini_inetd(htons(port), NULL);
1250 fprintf(logfile, "connected\n");
1253 struct client *c;
1255 c = create_client(0, port, moniker_str);
1256 /* close(0); */
1258 handleServer(c);
1260 free_client(c);
1263 krb5_free_context(context);
1265 return 0;