Use OpenSSL include path as needed
[heimdal.git] / appl / gssmask / gssmask.c
blobc3417001e9ccdc6c157c2291bb49e6090c61ae56
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 krb5_principal principal = NULL;
864 int fd;
866 ret32(c, flags);
867 retdata(c, pfxdata);
869 fd = mkstemp(fn + 5);
870 if (fd < 0)
871 errx(1, "mkstemp");
873 net_write(fd, pfxdata.data, pfxdata.length);
874 krb5_data_free(&pfxdata);
875 close(fd);
877 if (principal)
878 krb5_free_principal(context, principal);
880 put32(c, -1); /* hResource */
881 put32(c, GSMERR_NOT_SUPPORTED);
882 return 0;
885 static int
886 HandleOP(WrapExt)
888 OM_uint32 maj_stat, min_stat;
889 int32_t hContext, flags, bflags;
890 krb5_data token, header, trailer;
891 gss_ctx_id_t ctx;
892 unsigned char *p;
893 int conf_state, iov_len;
894 gss_iov_buffer_desc iov[6];
896 ret32(c, hContext);
897 ret32(c, flags);
898 ret32(c, bflags);
899 retdata(c, header);
900 retdata(c, token);
901 retdata(c, trailer);
903 ctx = find_handle(c->handles, hContext, handle_context);
904 if (ctx == NULL)
905 errx(1, "wrap: reference to unknown context");
907 memset(&iov, 0, sizeof(iov));
909 iov_len = sizeof(iov)/sizeof(iov[0]);
911 if (bflags & WRAP_EXP_ONLY_HEADER)
912 iov_len -= 2; /* skip trailer and padding, aka dce-style */
914 iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
915 if (header.length != 0) {
916 iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
917 iov[1].buffer.length = header.length;
918 iov[1].buffer.value = header.data;
919 } else {
920 iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
922 iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
923 iov[2].buffer.length = token.length;
924 iov[2].buffer.value = token.data;
925 if (trailer.length != 0) {
926 iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
927 iov[3].buffer.length = trailer.length;
928 iov[3].buffer.value = trailer.data;
929 } else {
930 iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
932 iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
933 iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
935 maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state,
936 iov, iov_len);
937 if (maj_stat != GSS_S_COMPLETE)
938 errx(1, "gss_wrap_iov_length failed");
940 maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state,
941 iov, iov_len);
942 if (maj_stat != GSS_S_COMPLETE)
943 errx(1, "gss_wrap_iov failed");
945 krb5_data_free(&token);
947 token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length;
948 token.data = malloc(token.length);
950 p = token.data;
951 memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
952 p += iov[0].buffer.length;
953 memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
954 p += iov[2].buffer.length;
955 memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
956 p += iov[4].buffer.length;
957 memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
958 p += iov[5].buffer.length;
960 gss_release_iov_buffer(NULL, iov, iov_len);
962 put32(c, 0); /* XXX fix gsm_error */
963 putdata(c, token);
965 free(token.data);
967 return 0;
971 static int
972 HandleOP(UnwrapExt)
974 OM_uint32 maj_stat, min_stat;
975 int32_t hContext, flags, bflags;
976 krb5_data token, header, trailer;
977 gss_ctx_id_t ctx;
978 gss_iov_buffer_desc iov[3];
979 int conf_state, iov_len;
980 gss_qop_t qop_state;
982 ret32(c, hContext);
983 ret32(c, flags);
984 ret32(c, bflags);
985 retdata(c, header);
986 retdata(c, token);
987 retdata(c, trailer);
989 iov_len = sizeof(iov)/sizeof(iov[0]);
991 if (bflags & WRAP_EXP_ONLY_HEADER)
992 iov_len -= 1; /* skip trailer and padding, aka dce-style */
994 ctx = find_handle(c->handles, hContext, handle_context);
995 if (ctx == NULL)
996 errx(1, "unwrap: reference to unknown context");
998 if (header.length != 0) {
999 iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
1000 iov[0].buffer.length = header.length;
1001 iov[0].buffer.value = header.data;
1002 } else {
1003 iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1005 iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
1006 iov[1].buffer.length = token.length;
1007 iov[1].buffer.value = token.data;
1009 if (trailer.length != 0) {
1010 iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
1011 iov[2].buffer.length = trailer.length;
1012 iov[2].buffer.value = trailer.data;
1013 } else {
1014 iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1017 maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state,
1018 iov, iov_len);
1020 if (maj_stat != GSS_S_COMPLETE)
1021 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
1023 if (maj_stat == GSS_S_COMPLETE) {
1024 token.data = iov[1].buffer.value;
1025 token.length = iov[1].buffer.length;
1026 } else {
1027 token.data = NULL;
1028 token.length = 0;
1030 put32(c, 0); /* XXX fix gsm_error */
1031 putdata(c, token);
1033 return 0;
1040 struct handler {
1041 enum gssMaggotOp op;
1042 const char *name;
1043 int (*func)(enum gssMaggotOp, struct client *);
1046 #define S(a) { e##a, #a, handle##a }
1048 struct handler handlers[] = {
1049 S(GetVersionInfo),
1050 S(GoodBye),
1051 S(InitContext),
1052 S(AcceptContext),
1053 S(ToastResource),
1054 S(AcquireCreds),
1055 S(Encrypt),
1056 S(Decrypt),
1057 S(Sign),
1058 S(Verify),
1059 S(GetVersionAndCapabilities),
1060 S(GetTargetName),
1061 S(SetLoggingSocket),
1062 S(ChangePassword),
1063 S(SetPasswordSelf),
1064 S(Wrap),
1065 S(Unwrap),
1066 S(ConnectLoggingService2),
1067 S(GetMoniker),
1068 S(CallExtension),
1069 S(AcquirePKInitCreds),
1070 S(WrapExt),
1071 S(UnwrapExt),
1074 #undef S
1080 static struct handler *
1081 find_op(int32_t op)
1083 int i;
1085 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
1086 if (handlers[i].op == op)
1087 return &handlers[i];
1088 return NULL;
1091 static struct client *
1092 create_client(krb5_socket_t sock, int port, const char *moniker)
1094 struct client *c;
1095 int ret;
1097 c = ecalloc(1, sizeof(*c));
1099 if (moniker) {
1100 c->moniker = estrdup(moniker);
1101 } else {
1102 char hostname[MAXHOSTNAMELEN];
1103 gethostname(hostname, sizeof(hostname));
1104 ret = asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
1105 if (ret == -1)
1106 c->moniker = NULL;
1109 if (!c->moniker)
1110 errx(1, "out of memory");
1113 c->salen = sizeof(c->sa);
1114 getpeername(sock, (struct sockaddr *)&c->sa, &c->salen);
1116 getnameinfo((struct sockaddr *)&c->sa, c->salen,
1117 c->servername, sizeof(c->servername),
1118 NULL, 0, NI_NUMERICHOST);
1121 c->sock = krb5_storage_from_socket(sock);
1122 if (c->sock == NULL)
1123 errx(1, "krb5_storage_from_socket");
1125 rk_closesocket(sock);
1127 return c;
1130 static void
1131 free_client(struct client *c)
1133 while(c->handles)
1134 del_handle(&c->handles, c->handles->idx);
1136 free(c->moniker);
1137 krb5_storage_free(c->sock);
1138 if (c->logging)
1139 krb5_storage_free(c->logging);
1140 free(c);
1144 static void *
1145 handleServer(void *ptr)
1147 struct handler *handler;
1148 struct client *c;
1149 int32_t op;
1151 c = (struct client *)ptr;
1154 while(1) {
1155 ret32(c, op);
1157 handler = find_op(op);
1158 if (handler == NULL) {
1159 logmessage(c, __FILE__, __LINE__, 0,
1160 "op %d not supported", (int)op);
1161 exit(1);
1164 logmessage(c, __FILE__, __LINE__, 0,
1165 "---> Got op %s from server %s",
1166 handler->name, c->servername);
1168 if ((handler->func)(handler->op, c))
1169 break;
1172 return NULL;
1176 static char *port_str;
1177 static int version_flag;
1178 static int help_flag;
1179 static char *logfile_str;
1180 static char *moniker_str;
1182 static int port = 4711;
1184 struct getargs args[] = {
1185 { "spn", 0, arg_string, &targetname, "This host's SPN",
1186 "service/host@REALM" },
1187 { "port", 'p', arg_string, &port_str, "Use this port",
1188 "number-of-service" },
1189 { "logfile", 0, arg_string, &logfile_str, "logfile",
1190 "number-of-service" },
1191 { "moniker", 0, arg_string, &moniker_str, "nickname",
1192 "name" },
1193 { "version", 0, arg_flag, &version_flag, "Print version",
1194 NULL },
1195 { "help", 0, arg_flag, &help_flag, NULL,
1196 NULL }
1199 static void
1200 usage(int ret)
1202 arg_printusage (args,
1203 sizeof(args) / sizeof(args[0]),
1204 NULL,
1205 "");
1206 exit (ret);
1210 main(int argc, char **argv)
1212 int optidx = 0;
1213 krb5_error_code ret;
1215 setprogname (argv[0]);
1217 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
1218 usage (1);
1220 if (help_flag)
1221 usage (0);
1223 if (version_flag) {
1224 print_version (NULL);
1225 return 0;
1228 if (optidx != argc)
1229 usage (1);
1231 if (port_str) {
1232 char *ptr;
1234 port = strtol (port_str, &ptr, 10);
1235 if (port == 0 && ptr == port_str)
1236 errx (1, "Bad port `%s'", port_str);
1239 ret = krb5_init_context(&context);
1240 if (ret)
1241 errx(1, "Error initializing kerberos: %d", ret);
1244 const char *lf = logfile_str;
1245 if (lf == NULL)
1246 lf = "/dev/tty";
1248 logfile = fopen(lf, "w");
1249 if (logfile == NULL)
1250 err(1, "error opening %s", lf);
1253 mini_inetd(htons(port), NULL);
1254 fprintf(logfile, "connected\n");
1257 struct client *c;
1259 c = create_client(0, port, moniker_str);
1260 /* close(0); */
1262 handleServer(c);
1264 free_client(c);
1267 krb5_free_context(context);
1269 return 0;