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.
41 enum handle_type
{ handle_context
, handle_cred
};
45 enum handle_type type
;
52 krb5_storage
*logging
;
55 struct handle
*handles
;
56 struct sockaddr_storage sa
;
58 char servername
[MAXHOSTNAMELEN
];
62 static char *targetname
;
70 logmessage(struct client
*c
, const char *file
, unsigned int lineno
,
71 int level
, const char *fmt
, ...)
78 vasprintf(&message
, fmt
, ap
);
82 fprintf(logfile
, "%s:%u: %d %s\n", file
, lineno
, level
, message
);
85 if (krb5_store_int32(c
->logging
, eLogInfo
) != 0)
86 errx(1, "krb5_store_int32: log level");
87 if (krb5_store_string(c
->logging
, file
) != 0)
88 errx(1, "krb5_store_string: filename");
89 if (krb5_store_int32(c
->logging
, lineno
) != 0)
90 errx(1, "krb5_store_string: filename");
91 if (krb5_store_string(c
->logging
, message
) != 0)
92 errx(1, "krb5_store_string: message");
93 if (krb5_ret_int32(c
->logging
, &ackid
) != 0)
94 errx(1, "krb5_ret_int32: ackid");
104 add_handle(struct client
*c
, enum handle_type type
, void *data
)
108 h
= ecalloc(1, sizeof(*h
));
110 h
->idx
= ++c
->nHandle
;
113 h
->next
= c
->handles
;
120 del_handle(struct handle
**h
, int32_t idx
)
128 if ((*h
)->idx
== idx
) {
129 struct handle
*p
= *h
;
132 case handle_context
: {
133 gss_ctx_id_t c
= p
->ptr
;
134 gss_delete_sec_context(&min_stat
, &c
, NULL
);
137 gss_cred_id_t c
= p
->ptr
;
138 gss_release_cred(&min_stat
, &c
);
146 errx(1, "tried to delete an unexisting handle");
150 find_handle(struct handle
*h
, int32_t idx
, enum handle_type type
)
159 errx(1, "monger switched type on handle!");
168 convert_gss_to_gsm(OM_uint32 maj_stat
)
173 case GSS_S_CONTINUE_NEEDED
:
174 return GSMERR_CONTINUE_NEEDED
;
175 case GSS_S_DEFECTIVE_TOKEN
:
176 return GSMERR_INVALID_TOKEN
;
178 return GSMERR_AP_MODIFIED
;
185 convert_krb5_to_gsm(krb5_error_code ret
)
200 acquire_cred(struct client
*c
,
201 krb5_principal principal
,
202 krb5_get_init_creds_opt
*opt
,
209 OM_uint32 maj_stat
, min_stat
;
213 krb5_get_init_creds_opt_set_forwardable (opt
, 1);
214 krb5_get_init_creds_opt_set_renew_life (opt
, 3600 * 24 * 30);
216 memset(&cred
, 0, sizeof(cred
));
218 ret
= krb5_get_init_creds_password (context
,
228 logmessage(c
, __FILE__
, __LINE__
, 0,
229 "krb5_get_init_creds failed: %d", ret
);
230 return convert_krb5_to_gsm(ret
);
233 ret
= krb5_cc_new_unique(context
, "MEMORY", NULL
, &id
);
235 krb5_err (context
, 1, ret
, "krb5_cc_initialize");
237 ret
= krb5_cc_initialize (context
, id
, cred
.client
);
239 krb5_err (context
, 1, ret
, "krb5_cc_initialize");
241 ret
= krb5_cc_store_cred (context
, id
, &cred
);
243 krb5_err (context
, 1, ret
, "krb5_cc_store_cred");
245 krb5_free_cred_contents (context
, &cred
);
247 maj_stat
= gss_krb5_import_cred(&min_stat
,
252 krb5_cc_close(context
, id
);
254 logmessage(c
, __FILE__
, __LINE__
, 0,
255 "krb5 import creds failed with: %d", maj_stat
);
256 return convert_gss_to_gsm(maj_stat
);
259 *handle
= add_handle(c
, handle_cred
, gcred
);
269 #define HandleOP(h) \
270 handle##h(enum gssMaggotOp op, struct client *c)
277 HandleOP(GetVersionInfo
)
279 put32(c
, GSSMAGGOTPROTOCOL
);
280 errx(1, "GetVersionInfo");
286 struct handle
*h
= c
->handles
;
295 logmessage(c
, __FILE__
, __LINE__
, 0,
296 "Did not toast all resources: %d", i
);
301 HandleOP(InitContext
)
303 OM_uint32 maj_stat
, min_stat
, ret_flags
;
304 int32_t hContext
, hCred
, flags
;
305 krb5_data target_name
, in_token
;
306 int32_t new_context_id
= 0, gsm_error
= 0;
307 krb5_data out_token
= { 0 , NULL
};
311 gss_name_t gss_target_name
;
312 gss_buffer_desc input_token
, output_token
;
313 gss_OID oid
= GSS_C_NO_OID
;
314 gss_buffer_t input_token_ptr
= GSS_C_NO_BUFFER
;
319 retdata(c
, target_name
);
320 retdata(c
, in_token
);
322 logmessage(c
, __FILE__
, __LINE__
, 0,
323 "targetname: <%.*s>", (int)target_name
.length
,
324 (char *)target_name
.data
);
326 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
329 creds
= find_handle(c
->handles
, hCred
, handle_cred
);
333 input_token
.length
= target_name
.length
;
334 input_token
.value
= target_name
.data
;
336 maj_stat
= gss_import_name(&min_stat
,
338 GSS_KRB5_NT_PRINCIPAL_NAME
,
340 if (GSS_ERROR(maj_stat
)) {
341 logmessage(c
, __FILE__
, __LINE__
, 0,
342 "import name creds failed with: %d", maj_stat
);
343 gsm_error
= convert_gss_to_gsm(maj_stat
);
349 if (in_token
.length
) {
350 input_token
.length
= in_token
.length
;
351 input_token
.value
= in_token
.data
;
352 input_token_ptr
= &input_token
;
354 krb5_errx(context
, 1, "initcreds, context NULL, but not first req");
356 input_token
.length
= 0;
357 input_token
.value
= NULL
;
359 krb5_errx(context
, 1, "initcreds, context not NULL, but first req");
362 if ((flags
& GSS_C_DELEG_FLAG
) != 0)
363 logmessage(c
, __FILE__
, __LINE__
, 0, "init_sec_context delegating");
364 if ((flags
& GSS_C_DCE_STYLE
) != 0)
365 logmessage(c
, __FILE__
, __LINE__
, 0, "init_sec_context dce-style");
367 maj_stat
= gss_init_sec_context(&min_stat
,
380 if (GSS_ERROR(maj_stat
)) {
382 del_handle(&c
->handles
, hContext
);
384 logmessage(c
, __FILE__
, __LINE__
, 0,
385 "gss_init_sec_context returns code: %d/%d",
388 if (input_token
.length
== 0)
389 new_context_id
= add_handle(c
, handle_context
, ctx
);
391 new_context_id
= hContext
;
394 gsm_error
= convert_gss_to_gsm(maj_stat
);
396 if (output_token
.length
) {
397 out_token
.data
= output_token
.value
;
398 out_token
.length
= output_token
.length
;
402 logmessage(c
, __FILE__
, __LINE__
, 0,
403 "InitContext return code: %d", gsm_error
);
405 put32(c
, new_context_id
);
407 putdata(c
, out_token
);
409 gss_release_name(&min_stat
, &gss_target_name
);
410 if (output_token
.length
)
411 gss_release_buffer(&min_stat
, &output_token
);
412 krb5_data_free(&in_token
);
413 krb5_data_free(&target_name
);
419 HandleOP(AcceptContext
)
421 OM_uint32 maj_stat
, min_stat
, ret_flags
;
422 int32_t hContext
, deleg_hcred
, flags
;
424 int32_t new_context_id
= 0, gsm_error
= 0;
425 krb5_data out_token
= { 0 , NULL
};
428 gss_cred_id_t deleg_cred
= GSS_C_NO_CREDENTIAL
;
429 gss_buffer_desc input_token
, output_token
;
430 gss_buffer_t input_token_ptr
= GSS_C_NO_BUFFER
;
434 retdata(c
, in_token
);
436 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
440 if (in_token
.length
) {
441 input_token
.length
= in_token
.length
;
442 input_token
.value
= in_token
.data
;
443 input_token_ptr
= &input_token
;
445 input_token
.length
= 0;
446 input_token
.value
= NULL
;
449 maj_stat
= gss_accept_sec_context(&min_stat
,
453 GSS_C_NO_CHANNEL_BINDINGS
,
460 if (GSS_ERROR(maj_stat
)) {
462 del_handle(&c
->handles
, hContext
);
463 logmessage(c
, __FILE__
, __LINE__
, 0,
464 "gss_accept_sec_context returns code: %d/%d",
469 new_context_id
= add_handle(c
, handle_context
, ctx
);
471 new_context_id
= hContext
;
473 if (output_token
.length
) {
474 out_token
.data
= output_token
.value
;
475 out_token
.length
= output_token
.length
;
477 if ((ret_flags
& GSS_C_DCE_STYLE
) != 0)
478 logmessage(c
, __FILE__
, __LINE__
, 0, "accept_sec_context dce-style");
479 if ((ret_flags
& GSS_C_DELEG_FLAG
) != 0) {
480 deleg_hcred
= add_handle(c
, handle_cred
, deleg_cred
);
481 logmessage(c
, __FILE__
, __LINE__
, 0,
482 "accept_context delegated handle: %d", deleg_hcred
);
484 gss_release_cred(&min_stat
, &deleg_cred
);
489 gsm_error
= convert_gss_to_gsm(maj_stat
);
491 put32(c
, new_context_id
);
493 putdata(c
, out_token
);
494 put32(c
, deleg_hcred
);
496 if (output_token
.length
)
497 gss_release_buffer(&min_stat
, &output_token
);
498 krb5_data_free(&in_token
);
504 HandleOP(ToastResource
)
509 logmessage(c
, __FILE__
, __LINE__
, 0, "toasting %d", handle
);
510 del_handle(&c
->handles
, handle
);
517 HandleOP(AcquireCreds
)
519 char *name
, *password
;
520 int32_t gsm_error
, flags
, handle
= 0;
521 krb5_principal principal
= NULL
;
522 krb5_get_init_creds_opt
*opt
= NULL
;
526 retstring(c
, password
);
529 logmessage(c
, __FILE__
, __LINE__
, 0,
530 "username: %s password: %s", name
, password
);
532 ret
= krb5_parse_name(context
, name
, &principal
);
534 gsm_error
= convert_krb5_to_gsm(ret
);
538 ret
= krb5_get_init_creds_opt_alloc (context
, &opt
);
540 krb5_err(context
, 1, ret
, "krb5_get_init_creds_opt_alloc");
542 krb5_get_init_creds_opt_set_pa_password(context
, opt
, password
, NULL
);
544 gsm_error
= acquire_cred(c
, principal
, opt
, &handle
);
547 logmessage(c
, __FILE__
, __LINE__
, 0,
548 "AcquireCreds handle: %d return code: %d", handle
, gsm_error
);
551 krb5_get_init_creds_opt_free (context
, opt
);
553 krb5_free_principal(context
, principal
);
566 OM_uint32 maj_stat
, min_stat
;
567 int32_t hContext
, flags
, seqno
;
570 gss_buffer_desc input_token
, output_token
;
577 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
579 errx(1, "sign: reference to unknown context");
581 input_token
.length
= token
.length
;
582 input_token
.value
= token
.data
;
584 maj_stat
= gss_get_mic(&min_stat
, ctx
, 0, &input_token
,
586 if (maj_stat
!= GSS_S_COMPLETE
)
587 errx(1, "gss_get_mic failed");
589 krb5_data_free(&token
);
591 token
.data
= output_token
.value
;
592 token
.length
= output_token
.length
;
594 put32(c
, 0); /* XXX fix gsm_error */
597 gss_release_buffer(&min_stat
, &output_token
);
605 OM_uint32 maj_stat
, min_stat
;
606 int32_t hContext
, flags
, seqno
;
609 gss_buffer_desc msg_token
, mic_token
;
614 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
616 errx(1, "verify: reference to unknown context");
622 msg_token
.length
= msg
.length
;
623 msg_token
.value
= msg
.data
;
627 mic_token
.length
= mic
.length
;
628 mic_token
.value
= mic
.data
;
630 maj_stat
= gss_verify_mic(&min_stat
, ctx
, &msg_token
,
632 if (maj_stat
!= GSS_S_COMPLETE
)
633 errx(1, "gss_verify_mic failed");
635 krb5_data_free(&mic
);
636 krb5_data_free(&msg
);
638 put32(c
, 0); /* XXX fix gsm_error */
644 HandleOP(GetVersionAndCapabilities
)
646 int32_t cap
= HAS_MONIKER
;
647 char name
[256] = "unknown", *str
;
650 cap
|= ISSERVER
; /* is server */
655 if (uname(&ut
) == 0) {
656 snprintf(name
, sizeof(name
), "%s-%s-%s",
657 ut
.sysname
, ut
.version
, ut
.machine
);
662 asprintf(&str
, "gssmask %s %s", PACKAGE_STRING
, name
);
664 put32(c
, GSSMAGGOTPROTOCOL
);
673 HandleOP(GetTargetName
)
676 putstring(c
, targetname
);
683 HandleOP(SetLoggingSocket
)
690 logmessage(c
, __FILE__
, __LINE__
, 0,
691 "logging port on peer is: %d", (int)portnum
);
693 socket_set_port((struct sockaddr
*)(&c
->sa
), htons(portnum
));
695 fd
= socket(((struct sockaddr
*)&c
->sa
)->sa_family
, SOCK_STREAM
, 0);
699 ret
= connect(fd
, (struct sockaddr
*)&c
->sa
, c
->salen
);
701 logmessage(c
, __FILE__
, __LINE__
, 0, "failed connect to log port: %s",
708 krb5_storage_free(c
->logging
);
709 c
->logging
= krb5_storage_from_fd(fd
);
712 krb5_store_int32(c
->logging
, eLogSetMoniker
);
713 store_string(c
->logging
, c
->moniker
);
715 logmessage(c
, __FILE__
, __LINE__
, 0, "logging turned on");
722 HandleOP(ChangePassword
)
724 errx(1, "ChangePassword");
728 HandleOP(SetPasswordSelf
)
730 errx(1, "SetPasswordSelf");
736 OM_uint32 maj_stat
, min_stat
;
737 int32_t hContext
, flags
, seqno
;
740 gss_buffer_desc input_token
, output_token
;
748 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
750 errx(1, "wrap: reference to unknown context");
752 input_token
.length
= token
.length
;
753 input_token
.value
= token
.data
;
755 maj_stat
= gss_wrap(&min_stat
, ctx
, flags
, 0, &input_token
,
756 &conf_state
, &output_token
);
757 if (maj_stat
!= GSS_S_COMPLETE
)
758 errx(1, "gss_wrap failed");
760 krb5_data_free(&token
);
762 token
.data
= output_token
.value
;
763 token
.length
= output_token
.length
;
765 put32(c
, 0); /* XXX fix gsm_error */
768 gss_release_buffer(&min_stat
, &output_token
);
777 OM_uint32 maj_stat
, min_stat
;
778 int32_t hContext
, flags
, seqno
;
781 gss_buffer_desc input_token
, output_token
;
790 ctx
= find_handle(c
->handles
, hContext
, handle_context
);
792 errx(1, "unwrap: reference to unknown context");
794 input_token
.length
= token
.length
;
795 input_token
.value
= token
.data
;
797 maj_stat
= gss_unwrap(&min_stat
, ctx
, &input_token
,
798 &output_token
, &conf_state
, &qop_state
);
800 if (maj_stat
!= GSS_S_COMPLETE
)
801 errx(1, "gss_unwrap failed: %d/%d", maj_stat
, min_stat
);
803 krb5_data_free(&token
);
804 if (maj_stat
== GSS_S_COMPLETE
) {
805 token
.data
= output_token
.value
;
806 token
.length
= output_token
.length
;
811 put32(c
, 0); /* XXX fix gsm_error */
814 if (maj_stat
== GSS_S_COMPLETE
)
815 gss_release_buffer(&min_stat
, &output_token
);
823 return handleWrap(op
, c
);
829 return handleUnwrap(op
, c
);
833 HandleOP(ConnectLoggingService2
)
835 errx(1, "ConnectLoggingService2");
841 putstring(c
, c
->moniker
);
846 HandleOP(CallExtension
)
848 errx(1, "CallExtension");
852 HandleOP(AcquirePKInitCreds
)
860 /* get credentials */
862 krb5_data_free(&pfxdata
);
864 put32(c
, -1); /* hResource */
865 put32(c
, GSMERR_NOT_SUPPORTED
);
876 int (*func
)(enum gssMaggotOp
, struct client
*);
879 #define S(a) { e##a, #a, handle##a }
881 struct handler handlers
[] = {
892 S(GetVersionAndCapabilities
),
899 S(ConnectLoggingService2
),
902 S(AcquirePKInitCreds
)
911 static struct handler
*
916 for (i
= 0; i
< sizeof(handlers
)/sizeof(handlers
[0]); i
++)
917 if (handlers
[i
].op
== op
)
922 static struct client
*
923 create_client(int fd
, int port
, const char *moniker
)
927 c
= ecalloc(1, sizeof(*c
));
930 c
->moniker
= estrdup(moniker
);
932 char hostname
[MAXHOSTNAMELEN
];
933 gethostname(hostname
, sizeof(hostname
));
934 asprintf(&c
->moniker
, "gssmask: %s:%d", hostname
, port
);
938 c
->salen
= sizeof(c
->sa
);
939 getpeername(fd
, (struct sockaddr
*)&c
->sa
, &c
->salen
);
941 getnameinfo((struct sockaddr
*)&c
->sa
, c
->salen
,
942 c
->servername
, sizeof(c
->servername
),
943 NULL
, 0, NI_NUMERICHOST
);
946 c
->sock
= krb5_storage_from_fd(fd
);
948 errx(1, "krb5_storage_from_fd");
956 free_client(struct client
*c
)
959 del_handle(&c
->handles
, c
->handles
->idx
);
962 krb5_storage_free(c
->sock
);
964 krb5_storage_free(c
->logging
);
970 handleServer(void *ptr
)
972 struct handler
*handler
;
976 c
= (struct client
*)ptr
;
982 handler
= find_op(op
);
983 if (handler
== NULL
) {
984 logmessage(c
, __FILE__
, __LINE__
, 0,
985 "op %d not supported", (int)op
);
989 logmessage(c
, __FILE__
, __LINE__
, 0,
990 "---> Got op %s from server %s",
991 handler
->name
, c
->servername
);
993 if ((handler
->func
)(handler
->op
, c
))
1001 static char *port_str
;
1002 static int version_flag
;
1003 static int help_flag
;
1004 static char *logfile_str
;
1005 static char *moniker_str
;
1007 static int port
= 4711;
1009 struct getargs args
[] = {
1010 { "spn", 0, arg_string
, &targetname
, "This host's SPN",
1011 "service/host@REALM" },
1012 { "port", 'p', arg_string
, &port_str
, "Use this port",
1013 "number-of-service" },
1014 { "logfile", 0, arg_string
, &logfile_str
, "logfile",
1015 "number-of-service" },
1016 { "moniker", 0, arg_string
, &moniker_str
, "nickname",
1018 { "version", 0, arg_flag
, &version_flag
, "Print version",
1020 { "help", 0, arg_flag
, &help_flag
, NULL
,
1027 arg_printusage (args
,
1028 sizeof(args
) / sizeof(args
[0]),
1035 main(int argc
, char **argv
)
1039 setprogname (argv
[0]);
1041 if (getarg (args
, sizeof(args
) / sizeof(args
[0]), argc
, argv
, &optidx
))
1048 print_version (NULL
);
1058 port
= strtol (port_str
, &ptr
, 10);
1059 if (port
== 0 && ptr
== port_str
)
1060 errx (1, "Bad port `%s'", port_str
);
1063 krb5_init_context(&context
);
1066 const char *lf
= logfile_str
;
1070 logfile
= fopen(lf
, "w");
1071 if (logfile
== NULL
)
1072 err(1, "error opening %s", lf
);
1075 mini_inetd(htons(port
));
1076 fprintf(logfile
, "connected\n");
1081 c
= create_client(0, port
, moniker_str
);
1089 krb5_free_context(context
);