Ensure DER form of hxtool ca random serial numbers
[heimdal.git] / appl / gssmask / gssmask.c
blobe90c1cab591257e2820c3c635ebb1181bb84538a
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, output_token;
316 gss_OID oid = GSS_C_NO_OID;
317 gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
319 ret32(c, hContext);
320 ret32(c, hCred);
321 ret32(c, flags);
322 retdata(c, target_name);
323 retdata(c, in_token);
325 logmessage(c, __FILE__, __LINE__, 0,
326 "targetname: <%.*s>", (int)target_name.length,
327 (char *)target_name.data);
329 ctx = find_handle(c->handles, hContext, handle_context);
330 if (ctx == NULL)
331 hContext = 0;
332 creds = find_handle(c->handles, hCred, handle_cred);
333 if (creds == NULL)
334 abort();
336 input_token.length = target_name.length;
337 input_token.value = target_name.data;
339 maj_stat = gss_import_name(&min_stat,
340 &input_token,
341 GSS_KRB5_NT_PRINCIPAL_NAME,
342 &gss_target_name);
343 if (GSS_ERROR(maj_stat)) {
344 logmessage(c, __FILE__, __LINE__, 0,
345 "import name creds failed with: %d", maj_stat);
346 gsm_error = convert_gss_to_gsm(maj_stat);
347 goto out;
350 /* oid from flags */
352 if (in_token.length) {
353 input_token.length = in_token.length;
354 input_token.value = in_token.data;
355 input_token_ptr = &input_token;
356 if (ctx == NULL)
357 krb5_errx(context, 1, "initcreds, context NULL, but not first req");
358 } else {
359 input_token.length = 0;
360 input_token.value = NULL;
361 if (ctx)
362 krb5_errx(context, 1, "initcreds, context not NULL, but first req");
365 if ((flags & GSS_C_DELEG_FLAG) != 0)
366 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating");
367 if ((flags & GSS_C_DCE_STYLE) != 0)
368 logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style");
370 maj_stat = gss_init_sec_context(&min_stat,
371 creds,
372 &ctx,
373 gss_target_name,
374 oid,
375 flags & 0x7f,
377 NULL,
378 input_token_ptr,
379 NULL,
380 &output_token,
381 &ret_flags,
382 NULL);
383 if (GSS_ERROR(maj_stat)) {
384 if (hContext != 0)
385 del_handle(&c->handles, hContext);
386 new_context_id = 0;
387 logmessage(c, __FILE__, __LINE__, 0,
388 "gss_init_sec_context returns code: %d/%d",
389 maj_stat, min_stat);
390 } else {
391 if (input_token.length == 0)
392 new_context_id = add_handle(c, handle_context, ctx);
393 else
394 new_context_id = hContext;
397 gsm_error = convert_gss_to_gsm(maj_stat);
399 if (output_token.length) {
400 out_token.data = output_token.value;
401 out_token.length = output_token.length;
404 out:
405 logmessage(c, __FILE__, __LINE__, 0,
406 "InitContext return code: %d", gsm_error);
408 put32(c, new_context_id);
409 put32(c, gsm_error);
410 putdata(c, out_token);
412 gss_release_name(&min_stat, &gss_target_name);
413 if (output_token.length)
414 gss_release_buffer(&min_stat, &output_token);
415 krb5_data_free(&in_token);
416 krb5_data_free(&target_name);
418 return 0;
421 static int
422 HandleOP(AcceptContext)
424 OM_uint32 maj_stat, min_stat, ret_flags;
425 int32_t hContext, deleg_hcred, flags;
426 krb5_data in_token;
427 int32_t new_context_id = 0, gsm_error = 0;
428 krb5_data out_token = { 0 , NULL };
430 gss_ctx_id_t ctx;
431 gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL;
432 gss_buffer_desc input_token, output_token;
434 ret32(c, hContext);
435 ret32(c, flags);
436 retdata(c, in_token);
438 ctx = find_handle(c->handles, hContext, handle_context);
439 if (ctx == NULL)
440 hContext = 0;
442 if (in_token.length) {
443 input_token.length = in_token.length;
444 input_token.value = in_token.data;
445 } else {
446 input_token.length = 0;
447 input_token.value = NULL;
450 maj_stat = gss_accept_sec_context(&min_stat,
451 &ctx,
452 GSS_C_NO_CREDENTIAL,
453 &input_token,
454 GSS_C_NO_CHANNEL_BINDINGS,
455 NULL,
456 NULL,
457 &output_token,
458 &ret_flags,
459 NULL,
460 &deleg_cred);
461 if (GSS_ERROR(maj_stat)) {
462 if (hContext != 0)
463 del_handle(&c->handles, hContext);
464 logmessage(c, __FILE__, __LINE__, 0,
465 "gss_accept_sec_context returns code: %d/%d",
466 maj_stat, min_stat);
467 new_context_id = 0;
468 } else {
469 if (hContext == 0)
470 new_context_id = add_handle(c, handle_context, ctx);
471 else
472 new_context_id = hContext;
474 if (output_token.length) {
475 out_token.data = output_token.value;
476 out_token.length = output_token.length;
478 if ((ret_flags & GSS_C_DCE_STYLE) != 0)
479 logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style");
480 if ((ret_flags & GSS_C_DELEG_FLAG) != 0) {
481 deleg_hcred = add_handle(c, handle_cred, deleg_cred);
482 logmessage(c, __FILE__, __LINE__, 0,
483 "accept_context delegated handle: %d", deleg_hcred);
484 } else {
485 gss_release_cred(&min_stat, &deleg_cred);
486 deleg_hcred = 0;
490 gsm_error = convert_gss_to_gsm(maj_stat);
492 put32(c, new_context_id);
493 put32(c, gsm_error);
494 putdata(c, out_token);
495 put32(c, deleg_hcred);
497 if (output_token.length)
498 gss_release_buffer(&min_stat, &output_token);
499 krb5_data_free(&in_token);
501 return 0;
504 static int
505 HandleOP(ToastResource)
507 int32_t handle;
509 ret32(c, handle);
510 logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle);
511 del_handle(&c->handles, handle);
512 put32(c, GSMERR_OK);
514 return 0;
517 static int
518 HandleOP(AcquireCreds)
520 char *name, *password;
521 int32_t gsm_error, flags, handle = 0;
522 krb5_principal principal = NULL;
523 krb5_get_init_creds_opt *opt = NULL;
524 krb5_error_code ret;
526 retstring(c, name);
527 retstring(c, password);
528 ret32(c, flags);
530 logmessage(c, __FILE__, __LINE__, 0,
531 "username: %s password: %s", name, password);
533 ret = krb5_parse_name(context, name, &principal);
534 if (ret) {
535 gsm_error = convert_krb5_to_gsm(ret);
536 goto out;
539 ret = krb5_get_init_creds_opt_alloc (context, &opt);
540 if (ret)
541 krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
543 krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL);
545 gsm_error = acquire_cred(c, principal, opt, &handle);
547 out:
548 logmessage(c, __FILE__, __LINE__, 0,
549 "AcquireCreds handle: %d return code: %d", handle, gsm_error);
551 if (opt)
552 krb5_get_init_creds_opt_free (context, opt);
553 if (principal)
554 krb5_free_principal(context, principal);
555 free(name);
556 free(password);
558 put32(c, gsm_error);
559 put32(c, handle);
561 return 0;
564 static int
565 HandleOP(Sign)
567 OM_uint32 maj_stat, min_stat;
568 int32_t hContext, flags, seqno;
569 krb5_data token;
570 gss_ctx_id_t ctx;
571 gss_buffer_desc input_token, output_token;
573 ret32(c, hContext);
574 ret32(c, flags);
575 ret32(c, seqno);
576 retdata(c, token);
578 ctx = find_handle(c->handles, hContext, handle_context);
579 if (ctx == NULL)
580 errx(1, "sign: reference to unknown context");
582 input_token.length = token.length;
583 input_token.value = token.data;
585 maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token,
586 &output_token);
587 if (maj_stat != GSS_S_COMPLETE)
588 errx(1, "gss_get_mic failed");
590 krb5_data_free(&token);
592 token.data = output_token.value;
593 token.length = output_token.length;
595 put32(c, 0); /* XXX fix gsm_error */
596 putdata(c, token);
598 gss_release_buffer(&min_stat, &output_token);
600 return 0;
603 static int
604 HandleOP(Verify)
606 OM_uint32 maj_stat, min_stat;
607 int32_t hContext, flags, seqno;
608 krb5_data msg, mic;
609 gss_ctx_id_t ctx;
610 gss_buffer_desc msg_token, mic_token;
611 gss_qop_t qop;
613 ret32(c, hContext);
615 ctx = find_handle(c->handles, hContext, handle_context);
616 if (ctx == NULL)
617 errx(1, "verify: reference to unknown context");
619 ret32(c, flags);
620 ret32(c, seqno);
621 retdata(c, msg);
623 msg_token.length = msg.length;
624 msg_token.value = msg.data;
626 retdata(c, mic);
628 mic_token.length = mic.length;
629 mic_token.value = mic.data;
631 maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token,
632 &mic_token, &qop);
633 if (maj_stat != GSS_S_COMPLETE)
634 errx(1, "gss_verify_mic failed");
636 krb5_data_free(&mic);
637 krb5_data_free(&msg);
639 put32(c, 0); /* XXX fix gsm_error */
641 return 0;
644 static int
645 HandleOP(GetVersionAndCapabilities)
647 int32_t cap = HAS_MONIKER;
648 char name[256] = "unknown", *str;
649 int ret;
651 if (targetname)
652 cap |= ISSERVER; /* is server */
654 #ifdef HAVE_UNAME
656 struct utsname ut;
657 if (uname(&ut) == 0) {
658 snprintf(name, sizeof(name), "%s-%s-%s",
659 ut.sysname, ut.version, ut.machine);
662 #endif
664 ret = asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name);
665 if (ret == -1)
666 errx(1, "out of memory");
668 put32(c, GSSMAGGOTPROTOCOL);
669 put32(c, cap);
670 putstring(c, str);
671 free(str);
673 return 0;
676 static int
677 HandleOP(GetTargetName)
679 if (targetname)
680 putstring(c, targetname);
681 else
682 putstring(c, "");
683 return 0;
686 static int
687 HandleOP(SetLoggingSocket)
689 int32_t portnum;
690 krb5_socket_t sock;
691 int ret;
693 ret32(c, portnum);
695 logmessage(c, __FILE__, __LINE__, 0,
696 "logging port on peer is: %d", (int)portnum);
698 socket_set_port((struct sockaddr *)(&c->sa), htons(portnum));
700 sock = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0);
701 if (sock == rk_INVALID_SOCKET)
702 return 0;
704 ret = connect(sock, (struct sockaddr *)&c->sa, c->salen);
705 if (ret < 0) {
706 logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s",
707 strerror(errno));
708 rk_closesocket(sock);
709 return 0;
712 if (c->logging)
713 krb5_storage_free(c->logging);
714 c->logging = krb5_storage_from_socket(sock);
715 rk_closesocket(sock);
717 krb5_store_int32(c->logging, eLogSetMoniker);
718 store_string(c->logging, c->moniker);
720 logmessage(c, __FILE__, __LINE__, 0, "logging turned on");
722 return 0;
726 static int
727 HandleOP(ChangePassword)
729 errx(1, "ChangePassword");
732 static int
733 HandleOP(SetPasswordSelf)
735 errx(1, "SetPasswordSelf");
738 static int
739 HandleOP(Wrap)
741 OM_uint32 maj_stat, min_stat;
742 int32_t hContext, flags, seqno;
743 krb5_data token;
744 gss_ctx_id_t ctx;
745 gss_buffer_desc input_token, output_token;
746 int conf_state;
748 ret32(c, hContext);
749 ret32(c, flags);
750 ret32(c, seqno);
751 retdata(c, token);
753 ctx = find_handle(c->handles, hContext, handle_context);
754 if (ctx == NULL)
755 errx(1, "wrap: reference to unknown context");
757 input_token.length = token.length;
758 input_token.value = token.data;
760 maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token,
761 &conf_state, &output_token);
762 if (maj_stat != GSS_S_COMPLETE)
763 errx(1, "gss_wrap failed");
765 krb5_data_free(&token);
767 token.data = output_token.value;
768 token.length = output_token.length;
770 put32(c, 0); /* XXX fix gsm_error */
771 putdata(c, token);
773 gss_release_buffer(&min_stat, &output_token);
775 return 0;
779 static int
780 HandleOP(Unwrap)
782 OM_uint32 maj_stat, min_stat;
783 int32_t hContext, flags, seqno;
784 krb5_data token;
785 gss_ctx_id_t ctx;
786 gss_buffer_desc input_token, output_token;
787 int conf_state;
788 gss_qop_t qop_state;
790 ret32(c, hContext);
791 ret32(c, flags);
792 ret32(c, seqno);
793 retdata(c, token);
795 ctx = find_handle(c->handles, hContext, handle_context);
796 if (ctx == NULL)
797 errx(1, "unwrap: reference to unknown context");
799 input_token.length = token.length;
800 input_token.value = token.data;
802 maj_stat = gss_unwrap(&min_stat, ctx, &input_token,
803 &output_token, &conf_state, &qop_state);
805 if (maj_stat != GSS_S_COMPLETE)
806 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
808 krb5_data_free(&token);
809 if (maj_stat == GSS_S_COMPLETE) {
810 token.data = output_token.value;
811 token.length = output_token.length;
812 } else {
813 token.data = NULL;
814 token.length = 0;
816 put32(c, 0); /* XXX fix gsm_error */
817 putdata(c, token);
819 if (maj_stat == GSS_S_COMPLETE)
820 gss_release_buffer(&min_stat, &output_token);
822 return 0;
825 static int
826 HandleOP(Encrypt)
828 return handleWrap(op, c);
831 static int
832 HandleOP(Decrypt)
834 return handleUnwrap(op, c);
837 static int
838 HandleOP(ConnectLoggingService2)
840 errx(1, "ConnectLoggingService2");
843 static int
844 HandleOP(GetMoniker)
846 putstring(c, c->moniker);
847 return 0;
850 static int
851 HandleOP(CallExtension)
853 errx(1, "CallExtension");
856 static int
857 HandleOP(AcquirePKInitCreds)
859 int32_t flags;
860 krb5_data pfxdata;
861 char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX";
862 krb5_principal principal = NULL;
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 if (principal)
877 krb5_free_principal(context, principal);
879 put32(c, -1); /* hResource */
880 put32(c, GSMERR_NOT_SUPPORTED);
881 return 0;
884 static int
885 HandleOP(WrapExt)
887 OM_uint32 maj_stat, min_stat;
888 int32_t hContext, flags, bflags;
889 krb5_data token, header, trailer;
890 gss_ctx_id_t ctx;
891 unsigned char *p;
892 int conf_state, iov_len;
893 gss_iov_buffer_desc iov[6];
895 ret32(c, hContext);
896 ret32(c, flags);
897 ret32(c, bflags);
898 retdata(c, header);
899 retdata(c, token);
900 retdata(c, trailer);
902 ctx = find_handle(c->handles, hContext, handle_context);
903 if (ctx == NULL)
904 errx(1, "wrap: reference to unknown context");
906 memset(&iov, 0, sizeof(iov));
908 iov_len = sizeof(iov)/sizeof(iov[0]);
910 if (bflags & WRAP_EXP_ONLY_HEADER)
911 iov_len -= 2; /* skip trailer and padding, aka dce-style */
913 iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
914 if (header.length != 0) {
915 iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
916 iov[1].buffer.length = header.length;
917 iov[1].buffer.value = header.data;
918 } else {
919 iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
921 iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
922 iov[2].buffer.length = token.length;
923 iov[2].buffer.value = token.data;
924 if (trailer.length != 0) {
925 iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
926 iov[3].buffer.length = trailer.length;
927 iov[3].buffer.value = trailer.data;
928 } else {
929 iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
931 iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
932 iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
934 maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state,
935 iov, iov_len);
936 if (maj_stat != GSS_S_COMPLETE)
937 errx(1, "gss_wrap_iov_length failed");
939 maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state,
940 iov, iov_len);
941 if (maj_stat != GSS_S_COMPLETE)
942 errx(1, "gss_wrap_iov failed");
944 krb5_data_free(&token);
946 token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length;
947 token.data = malloc(token.length);
949 p = token.data;
950 memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
951 p += iov[0].buffer.length;
952 memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
953 p += iov[2].buffer.length;
954 memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
955 p += iov[4].buffer.length;
956 memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
957 p += iov[5].buffer.length;
959 gss_release_iov_buffer(NULL, iov, iov_len);
961 put32(c, 0); /* XXX fix gsm_error */
962 putdata(c, token);
964 free(token.data);
966 return 0;
970 static int
971 HandleOP(UnwrapExt)
973 OM_uint32 maj_stat, min_stat;
974 int32_t hContext, flags, bflags;
975 krb5_data token, header, trailer;
976 gss_ctx_id_t ctx;
977 gss_iov_buffer_desc iov[3];
978 int conf_state, iov_len;
979 gss_qop_t qop_state;
981 ret32(c, hContext);
982 ret32(c, flags);
983 ret32(c, bflags);
984 retdata(c, header);
985 retdata(c, token);
986 retdata(c, trailer);
988 iov_len = sizeof(iov)/sizeof(iov[0]);
990 if (bflags & WRAP_EXP_ONLY_HEADER)
991 iov_len -= 1; /* skip trailer and padding, aka dce-style */
993 ctx = find_handle(c->handles, hContext, handle_context);
994 if (ctx == NULL)
995 errx(1, "unwrap: reference to unknown context");
997 if (header.length != 0) {
998 iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
999 iov[0].buffer.length = header.length;
1000 iov[0].buffer.value = header.data;
1001 } else {
1002 iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1004 iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
1005 iov[1].buffer.length = token.length;
1006 iov[1].buffer.value = token.data;
1008 if (trailer.length != 0) {
1009 iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
1010 iov[2].buffer.length = trailer.length;
1011 iov[2].buffer.value = trailer.data;
1012 } else {
1013 iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY;
1016 maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state,
1017 iov, iov_len);
1019 if (maj_stat != GSS_S_COMPLETE)
1020 errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
1022 if (maj_stat == GSS_S_COMPLETE) {
1023 token.data = iov[1].buffer.value;
1024 token.length = iov[1].buffer.length;
1025 } else {
1026 token.data = NULL;
1027 token.length = 0;
1029 put32(c, 0); /* XXX fix gsm_error */
1030 putdata(c, token);
1032 return 0;
1039 struct handler {
1040 enum gssMaggotOp op;
1041 const char *name;
1042 int (*func)(enum gssMaggotOp, struct client *);
1045 #define S(a) { e##a, #a, handle##a }
1047 struct handler handlers[] = {
1048 S(GetVersionInfo),
1049 S(GoodBye),
1050 S(InitContext),
1051 S(AcceptContext),
1052 S(ToastResource),
1053 S(AcquireCreds),
1054 S(Encrypt),
1055 S(Decrypt),
1056 S(Sign),
1057 S(Verify),
1058 S(GetVersionAndCapabilities),
1059 S(GetTargetName),
1060 S(SetLoggingSocket),
1061 S(ChangePassword),
1062 S(SetPasswordSelf),
1063 S(Wrap),
1064 S(Unwrap),
1065 S(ConnectLoggingService2),
1066 S(GetMoniker),
1067 S(CallExtension),
1068 S(AcquirePKInitCreds),
1069 S(WrapExt),
1070 S(UnwrapExt),
1073 #undef S
1079 static struct handler *
1080 find_op(int32_t op)
1082 int i;
1084 for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
1085 if (handlers[i].op == op)
1086 return &handlers[i];
1087 return NULL;
1090 static struct client *
1091 create_client(krb5_socket_t sock, int port, const char *moniker)
1093 struct client *c;
1094 int ret;
1096 c = ecalloc(1, sizeof(*c));
1098 if (moniker) {
1099 c->moniker = estrdup(moniker);
1100 } else {
1101 char hostname[MAXHOSTNAMELEN];
1102 gethostname(hostname, sizeof(hostname));
1103 ret = asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
1104 if (ret == -1)
1105 c->moniker = NULL;
1108 if (!c->moniker)
1109 errx(1, "out of memory");
1112 c->salen = sizeof(c->sa);
1113 getpeername(sock, (struct sockaddr *)&c->sa, &c->salen);
1115 getnameinfo((struct sockaddr *)&c->sa, c->salen,
1116 c->servername, sizeof(c->servername),
1117 NULL, 0, NI_NUMERICHOST);
1120 c->sock = krb5_storage_from_socket(sock);
1121 if (c->sock == NULL)
1122 errx(1, "krb5_storage_from_socket");
1124 rk_closesocket(sock);
1126 return c;
1129 static void
1130 free_client(struct client *c)
1132 while(c->handles)
1133 del_handle(&c->handles, c->handles->idx);
1135 free(c->moniker);
1136 krb5_storage_free(c->sock);
1137 if (c->logging)
1138 krb5_storage_free(c->logging);
1139 free(c);
1143 static void *
1144 handleServer(void *ptr)
1146 struct handler *handler;
1147 struct client *c;
1148 int32_t op;
1150 c = (struct client *)ptr;
1153 while(1) {
1154 ret32(c, op);
1156 handler = find_op(op);
1157 if (handler == NULL) {
1158 logmessage(c, __FILE__, __LINE__, 0,
1159 "op %d not supported", (int)op);
1160 exit(1);
1163 logmessage(c, __FILE__, __LINE__, 0,
1164 "---> Got op %s from server %s",
1165 handler->name, c->servername);
1167 if ((handler->func)(handler->op, c))
1168 break;
1171 return NULL;
1175 static char *port_str;
1176 static int version_flag;
1177 static int help_flag;
1178 static char *logfile_str;
1179 static char *moniker_str;
1181 static int port = 4711;
1183 struct getargs args[] = {
1184 { "spn", 0, arg_string, &targetname, "This host's SPN",
1185 "service/host@REALM" },
1186 { "port", 'p', arg_string, &port_str, "Use this port",
1187 "number-of-service" },
1188 { "logfile", 0, arg_string, &logfile_str, "logfile",
1189 "number-of-service" },
1190 { "moniker", 0, arg_string, &moniker_str, "nickname",
1191 "name" },
1192 { "version", 0, arg_flag, &version_flag, "Print version",
1193 NULL },
1194 { "help", 0, arg_flag, &help_flag, NULL,
1195 NULL }
1198 static void
1199 usage(int ret)
1201 arg_printusage (args,
1202 sizeof(args) / sizeof(args[0]),
1203 NULL,
1204 "");
1205 exit (ret);
1209 main(int argc, char **argv)
1211 int optidx = 0;
1213 setprogname (argv[0]);
1215 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
1216 usage (1);
1218 if (help_flag)
1219 usage (0);
1221 if (version_flag) {
1222 print_version (NULL);
1223 return 0;
1226 if (optidx != argc)
1227 usage (1);
1229 if (port_str) {
1230 char *ptr;
1232 port = strtol (port_str, &ptr, 10);
1233 if (port == 0 && ptr == port_str)
1234 errx (1, "Bad port `%s'", port_str);
1237 krb5_init_context(&context);
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;