2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
35 __RCSID("$Heimdal: gssmask.c 21229 2007-06-20 10:19:19Z lha $"
42 enum handle_type
{ handle_context
, handle_cred
};
46 enum handle_type type
;
53 krb5_storage
*logging
;
56 struct handle
*handles
;
57 struct sockaddr_storage sa
;
59 char servername
[MAXHOSTNAMELEN
];
63 static char *targetname
;
71 logmessage(struct client
*c
, const char *file
, unsigned int lineno
,
72 int level
, const char *fmt
, ...)
79 vasprintf(&message
, fmt
, ap
);
83 fprintf(logfile
, "%s:%u: %d %s\n", file
, lineno
, level
, message
);
86 if (krb5_store_int32(c
->logging
, eLogInfo
) != 0)
87 errx(1, "krb5_store_int32: log level");
88 if (krb5_store_string(c
->logging
, file
) != 0)
89 errx(1, "krb5_store_string: filename");
90 if (krb5_store_int32(c
->logging
, lineno
) != 0)
91 errx(1, "krb5_store_string: filename");
92 if (krb5_store_string(c
->logging
, message
) != 0)
93 errx(1, "krb5_store_string: message");
94 if (krb5_ret_int32(c
->logging
, &ackid
) != 0)
95 errx(1, "krb5_ret_int32: ackid");
105 add_handle(struct client
*c
, enum handle_type type
, void *data
)
109 h
= ecalloc(1, sizeof(*h
));
111 h
->idx
= ++c
->nHandle
;
114 h
->next
= c
->handles
;
121 del_handle(struct handle
**h
, int32_t idx
)
129 if ((*h
)->idx
== idx
) {
130 struct handle
*p
= *h
;
133 case handle_context
: {
134 gss_ctx_id_t c
= p
->ptr
;
135 gss_delete_sec_context(&min_stat
, &c
, NULL
);
138 gss_cred_id_t c
= p
->ptr
;
139 gss_release_cred(&min_stat
, &c
);
147 errx(1, "tried to delete an unexisting handle");
151 find_handle(struct handle
*h
, int32_t idx
, enum handle_type type
)
160 errx(1, "monger switched type on handle!");
169 convert_gss_to_gsm(OM_uint32 maj_stat
)
174 case GSS_S_CONTINUE_NEEDED
:
175 return GSMERR_CONTINUE_NEEDED
;
176 case GSS_S_DEFECTIVE_TOKEN
:
177 return GSMERR_INVALID_TOKEN
;
179 return GSMERR_AP_MODIFIED
;
186 convert_krb5_to_gsm(krb5_error_code ret
)
201 acquire_cred(struct client
*c
,
202 krb5_principal principal
,
203 krb5_get_init_creds_opt
*opt
,
210 OM_uint32 maj_stat
, min_stat
;
214 krb5_get_init_creds_opt_set_forwardable (opt
, 1);
215 krb5_get_init_creds_opt_set_renew_life (opt
, 3600 * 24 * 30);
217 memset(&cred
, 0, sizeof(cred
));
219 ret
= krb5_get_init_creds_password (context
,
229 logmessage(c
, __FILE__
, __LINE__
, 0,
230 "krb5_get_init_creds failed: %d", ret
);
231 return convert_krb5_to_gsm(ret
);
234 ret
= krb5_cc_new_unique(context
, "MEMORY", NULL
, &id
);
236 krb5_err (context
, 1, ret
, "krb5_cc_initialize");
238 ret
= krb5_cc_initialize (context
, id
, cred
.client
);
240 krb5_err (context
, 1, ret
, "krb5_cc_initialize");
242 ret
= krb5_cc_store_cred (context
, id
, &cred
);
244 krb5_err (context
, 1, ret
, "krb5_cc_store_cred");
246 krb5_free_cred_contents (context
, &cred
);
248 maj_stat
= gss_krb5_import_cred(&min_stat
,
253 krb5_cc_close(context
, id
);
255 logmessage(c
, __FILE__
, __LINE__
, 0,
256 "krb5 import creds failed with: %d", maj_stat
);
257 return convert_gss_to_gsm(maj_stat
);
260 *handle
= add_handle(c
, handle_cred
, gcred
);
270 #define HandleOP(h) \
271 handle##h(enum gssMaggotOp op, struct client *c)
278 HandleOP(GetVersionInfo
)
280 put32(c
, GSSMAGGOTPROTOCOL
);
281 errx(1, "GetVersionInfo");
287 struct handle
*h
= c
->handles
;
296 logmessage(c
, __FILE__
, __LINE__
, 0,
297 "Did not toast all resources: %d", i
);
302 HandleOP(InitContext
)
304 OM_uint32 maj_stat
, min_stat
, ret_flags
;
305 int32_t hContext
, hCred
, flags
;
306 krb5_data target_name
, in_token
;
307 int32_t new_context_id
= 0, gsm_error
= 0;
308 krb5_data out_token
= { 0 , NULL
};
312 gss_name_t gss_target_name
;
313 gss_buffer_desc input_token
, output_token
;
314 gss_OID oid
= GSS_C_NO_OID
;
315 gss_buffer_t input_token_ptr
= GSS_C_NO_BUFFER
;
320 retdata(c
, target_name
);
321 retdata(c
, in_token
);
323 logmessage(c
, __FILE__
, __LINE__
, 0,
324 "targetname: <%.*s>", (int)target_name
.length
,
325 (char *)target_name
.data
);
327 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
330 creds
= find_handle(c
->handles
, hCred
, handle_cred
);
334 input_token
.length
= target_name
.length
;
335 input_token
.value
= target_name
.data
;
337 maj_stat
= gss_import_name(&min_stat
,
339 GSS_KRB5_NT_PRINCIPAL_NAME
,
341 if (GSS_ERROR(maj_stat
)) {
342 logmessage(c
, __FILE__
, __LINE__
, 0,
343 "import name creds failed with: %d", maj_stat
);
344 gsm_error
= convert_gss_to_gsm(maj_stat
);
350 if (in_token
.length
) {
351 input_token
.length
= in_token
.length
;
352 input_token
.value
= in_token
.data
;
353 input_token_ptr
= &input_token
;
355 krb5_errx(context
, 1, "initcreds, context NULL, but not first req");
357 input_token
.length
= 0;
358 input_token
.value
= NULL
;
360 krb5_errx(context
, 1, "initcreds, context not NULL, but first req");
363 if ((flags
& GSS_C_DELEG_FLAG
) != 0)
364 logmessage(c
, __FILE__
, __LINE__
, 0, "init_sec_context delegating");
365 if ((flags
& GSS_C_DCE_STYLE
) != 0)
366 logmessage(c
, __FILE__
, __LINE__
, 0, "init_sec_context dce-style");
368 maj_stat
= gss_init_sec_context(&min_stat
,
381 if (GSS_ERROR(maj_stat
)) {
383 del_handle(&c
->handles
, hContext
);
385 logmessage(c
, __FILE__
, __LINE__
, 0,
386 "gss_init_sec_context returns code: %d/%d",
389 if (input_token
.length
== 0)
390 new_context_id
= add_handle(c
, handle_context
, ctx
);
392 new_context_id
= hContext
;
395 gsm_error
= convert_gss_to_gsm(maj_stat
);
397 if (output_token
.length
) {
398 out_token
.data
= output_token
.value
;
399 out_token
.length
= output_token
.length
;
403 logmessage(c
, __FILE__
, __LINE__
, 0,
404 "InitContext return code: %d", gsm_error
);
406 put32(c
, new_context_id
);
408 putdata(c
, out_token
);
410 gss_release_name(&min_stat
, &gss_target_name
);
411 if (output_token
.length
)
412 gss_release_buffer(&min_stat
, &output_token
);
413 krb5_data_free(&in_token
);
414 krb5_data_free(&target_name
);
420 HandleOP(AcceptContext
)
422 OM_uint32 maj_stat
, min_stat
, ret_flags
;
423 int32_t hContext
, deleg_hcred
, flags
;
425 int32_t new_context_id
= 0, gsm_error
= 0;
426 krb5_data out_token
= { 0 , NULL
};
429 gss_cred_id_t deleg_cred
= GSS_C_NO_CREDENTIAL
;
430 gss_buffer_desc input_token
, output_token
;
431 gss_buffer_t input_token_ptr
= GSS_C_NO_BUFFER
;
435 retdata(c
, in_token
);
437 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
441 if (in_token
.length
) {
442 input_token
.length
= in_token
.length
;
443 input_token
.value
= in_token
.data
;
444 input_token_ptr
= &input_token
;
446 input_token
.length
= 0;
447 input_token
.value
= NULL
;
450 maj_stat
= gss_accept_sec_context(&min_stat
,
454 GSS_C_NO_CHANNEL_BINDINGS
,
461 if (GSS_ERROR(maj_stat
)) {
463 del_handle(&c
->handles
, hContext
);
464 logmessage(c
, __FILE__
, __LINE__
, 0,
465 "gss_accept_sec_context returns code: %d/%d",
470 new_context_id
= add_handle(c
, handle_context
, ctx
);
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
);
485 gss_release_cred(&min_stat
, &deleg_cred
);
490 gsm_error
= convert_gss_to_gsm(maj_stat
);
492 put32(c
, new_context_id
);
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
);
505 HandleOP(ToastResource
)
510 logmessage(c
, __FILE__
, __LINE__
, 0, "toasting %d", handle
);
511 del_handle(&c
->handles
, handle
);
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
;
527 retstring(c
, password
);
530 logmessage(c
, __FILE__
, __LINE__
, 0,
531 "username: %s password: %s", name
, password
);
533 ret
= krb5_parse_name(context
, name
, &principal
);
535 gsm_error
= convert_krb5_to_gsm(ret
);
539 ret
= krb5_get_init_creds_opt_alloc (context
, &opt
);
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
);
548 logmessage(c
, __FILE__
, __LINE__
, 0,
549 "AcquireCreds handle: %d return code: %d", handle
, gsm_error
);
552 krb5_get_init_creds_opt_free (context
, opt
);
554 krb5_free_principal(context
, principal
);
567 OM_uint32 maj_stat
, min_stat
;
568 int32_t hContext
, flags
, seqno
;
571 gss_buffer_desc input_token
, output_token
;
578 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
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
,
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 */
598 gss_release_buffer(&min_stat
, &output_token
);
606 OM_uint32 maj_stat
, min_stat
;
607 int32_t hContext
, flags
, seqno
;
610 gss_buffer_desc msg_token
, mic_token
;
615 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
617 errx(1, "verify: reference to unknown context");
623 msg_token
.length
= msg
.length
;
624 msg_token
.value
= msg
.data
;
628 mic_token
.length
= mic
.length
;
629 mic_token
.value
= mic
.data
;
631 maj_stat
= gss_verify_mic(&min_stat
, ctx
, &msg_token
,
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 */
645 HandleOP(GetVersionAndCapabilities
)
647 int32_t cap
= HAS_MONIKER
;
648 char name
[256] = "unknown", *str
;
651 cap
|= ISSERVER
; /* is server */
656 if (uname(&ut
) == 0) {
657 snprintf(name
, sizeof(name
), "%s-%s-%s",
658 ut
.sysname
, ut
.version
, ut
.machine
);
663 asprintf(&str
, "gssmask %s %s", PACKAGE_STRING
, name
);
665 put32(c
, GSSMAGGOTPROTOCOL
);
674 HandleOP(GetTargetName
)
677 putstring(c
, targetname
);
684 HandleOP(SetLoggingSocket
)
691 logmessage(c
, __FILE__
, __LINE__
, 0,
692 "logging port on peer is: %d", (int)portnum
);
694 socket_set_port((struct sockaddr
*)(&c
->sa
), htons(portnum
));
696 fd
= socket(((struct sockaddr
*)&c
->sa
)->sa_family
, SOCK_STREAM
, 0);
700 ret
= connect(fd
, (struct sockaddr
*)&c
->sa
, c
->salen
);
702 logmessage(c
, __FILE__
, __LINE__
, 0, "failed connect to log port: %s",
709 krb5_storage_free(c
->logging
);
710 c
->logging
= krb5_storage_from_fd(fd
);
713 krb5_store_int32(c
->logging
, eLogSetMoniker
);
714 store_string(c
->logging
, c
->moniker
);
716 logmessage(c
, __FILE__
, __LINE__
, 0, "logging turned on");
723 HandleOP(ChangePassword
)
725 errx(1, "ChangePassword");
729 HandleOP(SetPasswordSelf
)
731 errx(1, "SetPasswordSelf");
737 OM_uint32 maj_stat
, min_stat
;
738 int32_t hContext
, flags
, seqno
;
741 gss_buffer_desc input_token
, output_token
;
749 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
751 errx(1, "wrap: reference to unknown context");
753 input_token
.length
= token
.length
;
754 input_token
.value
= token
.data
;
756 maj_stat
= gss_wrap(&min_stat
, ctx
, flags
, 0, &input_token
,
757 &conf_state
, &output_token
);
758 if (maj_stat
!= GSS_S_COMPLETE
)
759 errx(1, "gss_wrap failed");
761 krb5_data_free(&token
);
763 token
.data
= output_token
.value
;
764 token
.length
= output_token
.length
;
766 put32(c
, 0); /* XXX fix gsm_error */
769 gss_release_buffer(&min_stat
, &output_token
);
778 OM_uint32 maj_stat
, min_stat
;
779 int32_t hContext
, flags
, seqno
;
782 gss_buffer_desc input_token
, output_token
;
791 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
793 errx(1, "unwrap: reference to unknown context");
795 input_token
.length
= token
.length
;
796 input_token
.value
= token
.data
;
798 maj_stat
= gss_unwrap(&min_stat
, ctx
, &input_token
,
799 &output_token
, &conf_state
, &qop_state
);
801 if (maj_stat
!= GSS_S_COMPLETE
)
802 errx(1, "gss_unwrap failed: %d/%d", maj_stat
, min_stat
);
804 krb5_data_free(&token
);
805 if (maj_stat
== GSS_S_COMPLETE
) {
806 token
.data
= output_token
.value
;
807 token
.length
= output_token
.length
;
812 put32(c
, 0); /* XXX fix gsm_error */
815 if (maj_stat
== GSS_S_COMPLETE
)
816 gss_release_buffer(&min_stat
, &output_token
);
824 return handleWrap(op
, c
);
830 return handleUnwrap(op
, c
);
834 HandleOP(ConnectLoggingService2
)
836 errx(1, "ConnectLoggingService2");
842 putstring(c
, c
->moniker
);
847 HandleOP(CallExtension
)
849 errx(1, "CallExtension");
853 HandleOP(AcquirePKInitCreds
)
861 /* get credentials */
863 krb5_data_free(&pfxdata
);
865 put32(c
, -1); /* hResource */
866 put32(c
, GSMERR_NOT_SUPPORTED
);
877 int (*func
)(enum gssMaggotOp
, struct client
*);
880 #define S(a) { e##a, #a, handle##a }
882 struct handler handlers
[] = {
893 S(GetVersionAndCapabilities
),
900 S(ConnectLoggingService2
),
903 S(AcquirePKInitCreds
)
912 static struct handler
*
917 for (i
= 0; i
< sizeof(handlers
)/sizeof(handlers
[0]); i
++)
918 if (handlers
[i
].op
== op
)
923 static struct client
*
924 create_client(int fd
, int port
, const char *moniker
)
928 c
= ecalloc(1, sizeof(*c
));
931 c
->moniker
= estrdup(moniker
);
933 char hostname
[MAXHOSTNAMELEN
];
934 gethostname(hostname
, sizeof(hostname
));
935 asprintf(&c
->moniker
, "gssmask: %s:%d", hostname
, port
);
939 c
->salen
= sizeof(c
->sa
);
940 getpeername(fd
, (struct sockaddr
*)&c
->sa
, &c
->salen
);
942 getnameinfo((struct sockaddr
*)&c
->sa
, c
->salen
,
943 c
->servername
, sizeof(c
->servername
),
944 NULL
, 0, NI_NUMERICHOST
);
947 c
->sock
= krb5_storage_from_fd(fd
);
949 errx(1, "krb5_storage_from_fd");
957 free_client(struct client
*c
)
960 del_handle(&c
->handles
, c
->handles
->idx
);
963 krb5_storage_free(c
->sock
);
965 krb5_storage_free(c
->logging
);
971 handleServer(void *ptr
)
973 struct handler
*handler
;
977 c
= (struct client
*)ptr
;
983 handler
= find_op(op
);
984 if (handler
== NULL
) {
985 logmessage(c
, __FILE__
, __LINE__
, 0,
986 "op %d not supported", (int)op
);
990 logmessage(c
, __FILE__
, __LINE__
, 0,
991 "---> Got op %s from server %s",
992 handler
->name
, c
->servername
);
994 if ((handler
->func
)(handler
->op
, c
))
1002 static char *port_str
;
1003 static int version_flag
;
1004 static int help_flag
;
1005 static char *logfile_str
;
1006 static char *moniker_str
;
1008 static int port
= 4711;
1010 struct getargs args
[] = {
1011 { "spn", 0, arg_string
, &targetname
, "This host's SPN",
1012 "service/host@REALM" },
1013 { "port", 'p', arg_string
, &port_str
, "Use this port",
1014 "number-of-service" },
1015 { "logfile", 0, arg_string
, &logfile_str
, "logfile",
1016 "number-of-service" },
1017 { "moniker", 0, arg_string
, &moniker_str
, "nickname",
1019 { "version", 0, arg_flag
, &version_flag
, "Print version",
1021 { "help", 0, arg_flag
, &help_flag
, NULL
,
1028 arg_printusage (args
,
1029 sizeof(args
) / sizeof(args
[0]),
1036 main(int argc
, char **argv
)
1040 setprogname (argv
[0]);
1042 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
, &optidx
))
1049 print_version (NULL
);
1059 port
= strtol (port_str
, &ptr
, 10);
1060 if (port
== 0 && ptr
== port_str
)
1061 errx (1, "Bad port `%s'", port_str
);
1064 krb5_init_context(&context
);
1067 const char *lf
= logfile_str
;
1071 logfile
= fopen(lf
, "w");
1072 if (logfile
== NULL
)
1073 err(1, "error opening %s", lf
);
1076 mini_inetd(htons(port
));
1077 fprintf(logfile
, "connected\n");
1082 c
= create_client(0, port
, moniker_str
);
1090 krb5_free_context(context
);