8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / telnet / kerberos5.c
blobea624720615a53f708a939360e1d8cfd8193ec22
1 /*
2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 #pragma ident "%Z%%M% %I% %E% SMI"
8 /*
9 * usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
11 * Copyright (c) 1991, 1993
12 * The Regents of the University of California. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by the University of
25 * California, Berkeley and its contributors.
26 * 4. Neither the name of the University nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
43 /* based on @(#)kerberos5.c 8.1 (Berkeley) 6/4/93 */
46 * Copyright (C) 1990 by the Massachusetts Institute of Technology
48 * Export of this software from the United States of America may
49 * require a specific license from the United States Government.
50 * It is the responsibility of any person or organization contemplating
51 * export to obtain such a license before exporting.
53 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
54 * distribute this software and its documentation for any purpose and
55 * without fee is hereby granted, provided that the above copyright
56 * notice appear in all copies and that both that copyright notice and
57 * this permission notice appear in supporting documentation, and that
58 * the name of M.I.T. not be used in advertising or publicity pertaining
59 * to distribution of the software without specific, written prior
60 * permission. Furthermore if you modify this software you must label
61 * your software as modified software and not distribute it in such a
62 * fashion that it might be confused with the original M.I.T. software.
63 * M.I.T. makes no representations about the suitability of
64 * this software for any purpose. It is provided "as is" without express
65 * or implied warranty.
69 #include <arpa/telnet.h>
70 #include <stdio.h>
71 #include <ctype.h>
72 #include <syslog.h>
73 #include <stdlib.h>
75 /* the following are from the kerberos tree */
76 #include <k5-int.h>
77 #include <com_err.h>
78 #include <netdb.h>
79 #include <profile/prof_int.h>
80 #include <sys/param.h>
81 #include "externs.h"
83 extern char *RemoteHostName;
84 extern boolean_t auth_debug_mode;
85 extern int net;
87 #define ACCEPTED_ENCTYPE(a) \
88 (a == ENCTYPE_DES_CBC_CRC || a == ENCTYPE_DES_CBC_MD5)
89 /* for comapatibility with non-Solaris KDC's, this has to be big enough */
90 #define KERBEROS_BUFSIZ 8192
92 int forward_flags = 0; /* Flags get set in telnet/main.c on -f and -F */
93 static void kerberos5_forward(Authenticator *);
95 static unsigned char str_data[KERBEROS_BUFSIZ] = { IAC, SB,
96 TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, };
97 static char *appdef[] = { "appdefaults", "telnet", NULL };
98 static char *realmdef[] = { "realms", NULL, "telnet", NULL };
100 static krb5_auth_context auth_context = 0;
102 static krb5_data auth; /* telnetd gets session key from here */
103 static krb5_ticket *ticket = NULL;
104 /* telnet matches the AP_REQ and AP_REP with this */
106 static krb5_keyblock *session_key = 0;
107 char *telnet_krb5_realm = NULL;
110 * Change the kerberos realm
112 void
113 set_krb5_realm(char *name)
115 if (name == NULL) {
116 (void) fprintf(stderr, gettext("Could not set Kerberos realm, "
117 "no realm provided.\n"));
118 return;
121 if (telnet_krb5_realm)
122 free(telnet_krb5_realm);
124 telnet_krb5_realm = (char *)strdup(name);
126 if (telnet_krb5_realm == NULL)
127 (void) fprintf(stderr, gettext(
128 "Could not set Kerberos realm, malloc failed\n"));
131 #define RETURN_NOMEM { errno = ENOMEM; return (-1); }
133 static int
134 krb5_send_data(Authenticator *ap, int type, krb5_pointer d, int c)
136 /* the first 3 bytes are control chars */
137 unsigned char *p = str_data + 4;
138 unsigned char *cd = (unsigned char *)d;
139 /* spaceleft is incremented whenever p is decremented */
140 size_t spaceleft = sizeof (str_data) - 4;
142 if (c == -1)
143 c = strlen((char *)cd);
145 if (auth_debug_mode) {
146 (void) printf("%s:%d: [%d] (%d)",
147 str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
148 str_data[3], type, c);
149 printd(d, c);
150 (void) printf("\r\n");
153 if (spaceleft < 3)
154 RETURN_NOMEM;
155 *p++ = ap->type;
156 *p++ = ap->way;
157 *p++ = type;
158 spaceleft -= 3;
160 while (c-- > 0) {
161 if (spaceleft < 2)
162 RETURN_NOMEM;
163 if ((*p++ = *cd++) == IAC) {
164 *p++ = IAC;
165 spaceleft -= 2;
169 if (spaceleft < 2)
170 RETURN_NOMEM;
171 *p++ = IAC;
172 *p++ = SE;
173 if (str_data[3] == TELQUAL_IS)
174 printsub('>', &str_data[2], p - &str_data[2]);
175 return (net_write(str_data, p - str_data));
178 krb5_context telnet_context = 0;
180 /* ARGSUSED */
182 kerberos5_init(Authenticator *ap)
184 krb5_error_code retval;
186 str_data[3] = TELQUAL_IS;
187 if (krb5auth_flag && (telnet_context == 0)) {
188 retval = krb5_init_context(&telnet_context);
189 if (retval)
190 return (0);
192 return (1);
196 kerberos5_send(Authenticator *ap)
198 krb5_error_code retval;
199 krb5_ccache ccache;
200 krb5_creds creds; /* telnet gets session key from here */
201 krb5_creds *new_creds = 0;
202 int ap_opts;
203 char type_check[2];
204 krb5_data check_data;
206 krb5_keyblock *newkey = 0;
208 int i;
209 krb5_enctype *ktypes;
211 if (!UserNameRequested) {
212 if (auth_debug_mode)
213 (void) printf(gettext("telnet: Kerberos V5: "
214 "no user name supplied\r\n"));
215 return (0);
218 if ((retval = krb5_cc_default(telnet_context, &ccache))) {
219 if (auth_debug_mode)
220 (void) printf(gettext("telnet: Kerberos V5: "
221 "could not get default ccache\r\n"));
222 return (0);
225 (void) memset((char *)&creds, 0, sizeof (creds));
226 if (auth_debug_mode)
227 printf("telnet: calling krb5_sname_to_principal\n");
228 if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
229 "host", KRB5_NT_SRV_HST, &creds.server))) {
230 if (auth_debug_mode)
231 (void) printf(gettext("telnet: Kerberos V5: error "
232 "while constructing service name: %s\r\n"),
233 error_message(retval));
234 return (0);
236 if (auth_debug_mode)
237 printf("telnet: done calling krb5_sname_to_principal\n");
239 if (telnet_krb5_realm != NULL) {
240 krb5_data rdata;
242 rdata.magic = 0;
243 rdata.length = strlen(telnet_krb5_realm);
244 rdata.data = (char *)malloc(rdata.length + 1);
245 if (rdata.data == NULL) {
246 (void) fprintf(stderr, gettext("malloc failed\n"));
247 return (0);
249 (void) strcpy(rdata.data, telnet_krb5_realm);
250 krb5_princ_set_realm(telnet_context, creds.server, &rdata);
251 if (auth_debug_mode)
252 (void) printf(gettext(
253 "telnet: Kerberos V5: set kerberos realm to %s\r\n"),
254 telnet_krb5_realm);
257 if ((retval = krb5_cc_get_principal(telnet_context, ccache,
258 &creds.client)) != NULL) {
259 if (auth_debug_mode) {
260 (void) printf(gettext(
261 "telnet: Kerberos V5: failure on principal "
262 "(%s)\r\n"), error_message(retval));
264 krb5_free_cred_contents(telnet_context, &creds);
265 return (0);
268 * Check to to confirm that at least one of the supported
269 * encryption types (des-cbc-md5, des-cbc-crc is available. If
270 * one is available then use it to obtain credentials.
273 if ((retval = krb5_get_tgs_ktypes(telnet_context, creds.server,
274 &ktypes))) {
275 if (auth_debug_mode) {
276 (void) printf(gettext(
277 "telnet: Kerberos V5: could not determine "
278 "TGS encryption types "
279 "(see default_tgs_enctypes in krb5.conf) "
280 "(%s)\r\n"), error_message(retval));
282 krb5_free_cred_contents(telnet_context, &creds);
283 return (0);
286 for (i = 0; ktypes[i]; i++) {
287 if (ACCEPTED_ENCTYPE(ktypes[i]))
288 break;
291 if (ktypes[i] == 0) {
292 if (auth_debug_mode) {
293 (void) printf(gettext(
294 "telnet: Kerberos V5: "
295 "failure on encryption types. "
296 "Cannot find des-cbc-md5 or des-cbc-crc "
297 "in list of TGS encryption types "
298 "(see default_tgs_enctypes in krb5.conf)\n"));
300 krb5_free_cred_contents(telnet_context, &creds);
301 return (0);
304 creds.keyblock.enctype = ktypes[i];
305 if ((retval = krb5_get_credentials(telnet_context, 0,
306 ccache, &creds, &new_creds))) {
307 if (auth_debug_mode) {
308 (void) printf(gettext(
309 "telnet: Kerberos V5: failure on credentials "
310 "(%s)\r\n"), error_message(retval));
312 krb5_free_cred_contents(telnet_context, &creds);
313 return (0);
316 ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
317 AP_OPTS_MUTUAL_REQUIRED : 0;
319 ap_opts |= AP_OPTS_USE_SUBKEY;
321 if (auth_context) {
322 krb5_auth_con_free(telnet_context, auth_context);
323 auth_context = 0;
325 if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) {
326 if (auth_debug_mode) {
327 (void) printf(gettext(
328 "Kerberos V5: failed to init auth_context "
329 "(%s)\r\n"), error_message(retval));
331 return (0);
334 krb5_auth_con_setflags(telnet_context, auth_context,
335 KRB5_AUTH_CONTEXT_RET_TIME);
337 type_check[0] = ap->type;
338 type_check[1] = ap->way;
339 check_data.magic = KV5M_DATA;
340 check_data.length = 2;
341 check_data.data = (char *)&type_check;
343 retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
344 &check_data, new_creds, &auth);
346 krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
347 if (session_key) {
348 krb5_free_keyblock(telnet_context, session_key);
349 session_key = 0;
352 if (newkey) {
354 * keep the key in our private storage, but don't use it
355 * yet---see kerberos5_reply() below
357 if (!(ACCEPTED_ENCTYPE(newkey->enctype))) {
358 if (!(ACCEPTED_ENCTYPE(new_creds->keyblock.enctype)))
359 /* use the session key in credentials instead */
360 krb5_copy_keyblock(telnet_context,
361 &new_creds->keyblock, &session_key);
362 } else
363 krb5_copy_keyblock(telnet_context,
364 newkey, &session_key);
366 krb5_free_keyblock(telnet_context, newkey);
369 krb5_free_cred_contents(telnet_context, &creds);
370 krb5_free_creds(telnet_context, new_creds);
372 if (retval) {
373 if (auth_debug_mode)
374 (void) printf(gettext(
375 "telnet: Kerberos V5: mk_req failed (%s)\r\n"),
376 error_message(retval));
377 return (0);
380 if ((auth_sendname((uchar_t *)UserNameRequested,
381 strlen(UserNameRequested))) == NULL) {
382 if (auth_debug_mode)
383 (void) printf(gettext(
384 "telnet: Not enough room for user name\r\n"));
385 return (0);
387 retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length);
388 if (auth_debug_mode && retval) {
389 (void) printf(gettext(
390 "telnet: Sent Kerberos V5 credentials to server\r\n"));
391 } else if (auth_debug_mode) {
392 (void) printf(gettext(
393 "telnet: Not enough room for authentication data\r\n"));
394 return (0);
396 return (1);
399 void
400 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
402 Session_Key skey;
403 static boolean_t mutual_complete = B_FALSE;
405 if (cnt-- < 1)
406 return;
407 switch (*data++) {
408 case KRB_REJECT:
409 if (cnt > 0)
410 (void) printf(gettext(
411 "[ Kerberos V5 refuses authentication because "
412 "%.*s ]\r\n"), cnt, data);
413 else
414 (void) printf(gettext(
415 "[ Kerberos V5 refuses authentication ]\r\n"));
416 auth_send_retry();
417 return;
418 case KRB_ACCEPT:
419 if (!mutual_complete) {
420 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
421 (void) printf(gettext(
422 "[ Kerberos V5 accepted you, but didn't "
423 "provide mutual authentication! ]\r\n"));
424 auth_send_retry();
425 return;
428 if (session_key) {
429 skey.type = SK_DES;
430 skey.length = 8;
431 skey.data = session_key->contents;
432 encrypt_session_key(&skey);
435 if (cnt)
436 (void) printf(gettext(
437 "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"),
438 cnt, data);
439 else
440 (void) printf(gettext(
441 "[ Kerberos V5 accepts you ]\r\n"));
442 auth_finished(ap, AUTH_USER);
444 if (forward_flags & OPTS_FORWARD_CREDS)
445 kerberos5_forward(ap);
447 break;
448 case KRB_RESPONSE:
449 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
450 /* the rest of the reply should contain a krb_ap_rep */
451 krb5_ap_rep_enc_part *reply;
452 krb5_data inbuf;
453 krb5_error_code retval;
455 inbuf.length = cnt;
456 inbuf.data = (char *)data;
458 retval = krb5_rd_rep(telnet_context, auth_context,
459 &inbuf, &reply);
460 if (retval) {
461 (void) printf(gettext(
462 "[ Mutual authentication failed: "
463 "%s ]\r\n"), error_message(retval));
464 auth_send_retry();
465 return;
467 krb5_free_ap_rep_enc_part(telnet_context, reply);
469 if (session_key) {
470 skey.type = SK_DES;
471 skey.length = 8;
472 skey.data = session_key->contents;
473 encrypt_session_key(&skey);
475 mutual_complete = B_TRUE;
477 return;
478 case KRB_FORWARD_ACCEPT:
479 (void) printf(gettext(
480 "[ Kerberos V5 accepted forwarded credentials ]\r\n"));
481 return;
482 case KRB_FORWARD_REJECT:
483 (void) printf(gettext(
484 "[ Kerberos V5 refuses forwarded credentials because "
485 "%.*s ]\r\n"), cnt, data);
486 return;
487 default:
488 if (auth_debug_mode)
489 (void) printf(gettext(
490 "Unknown Kerberos option %d\r\n"), data[-1]);
491 return;
495 /* ARGSUSED */
497 kerberos5_status(Authenticator *ap, char *name, int level)
499 if (level < AUTH_USER)
500 return (level);
502 if (UserNameRequested && krb5_kuserok(telnet_context,
503 ticket->enc_part2->client, UserNameRequested)) {
505 /* the name buffer comes from telnetd/telnetd{-ktd}.c */
506 (void) strncpy(name, UserNameRequested, MAXNAMELEN);
507 name[MAXNAMELEN-1] = '\0';
508 return (AUTH_VALID);
509 } else
510 return (AUTH_USER);
513 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len); }
514 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len); }
517 * Used with the set opt command to print suboptions
519 void
520 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
522 char lbuf[AUTH_LBUF_BUFSIZ];
523 register int i;
525 buf[buflen-1] = '\0'; /* make sure its NULL terminated */
526 buflen -= 1;
528 switch (data[3]) {
529 case KRB_REJECT: /* Rejected (reason might follow) */
530 (void) strncpy((char *)buf, " REJECT ", buflen);
531 goto common;
533 case KRB_ACCEPT: /* Accepted (name might follow) */
534 (void) strncpy((char *)buf, " ACCEPT ", buflen);
535 common:
536 BUMP(buf, buflen);
537 if (cnt <= 4)
538 break;
539 ADDC(buf, buflen, '"');
540 for (i = 4; i < cnt; i++)
541 ADDC(buf, buflen, data[i]);
542 ADDC(buf, buflen, '"');
543 ADDC(buf, buflen, '\0');
544 break;
546 case KRB_AUTH: /* Authentication data follows */
547 (void) strncpy((char *)buf, " AUTH", buflen);
548 goto common2;
550 case KRB_RESPONSE:
551 (void) strncpy((char *)buf, " RESPONSE", buflen);
552 goto common2;
554 case KRB_FORWARD: /* Forwarded credentials follow */
555 (void) strncpy((char *)buf, " FORWARD", buflen);
556 goto common2;
558 case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */
559 (void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen);
560 goto common2;
562 case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */
563 /* (reason might follow) */
564 (void) strncpy((char *)buf, " FORWARD_REJECT", buflen);
565 goto common2;
567 default:
568 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ,
569 gettext(" %d (unknown)"),
570 data[3]);
571 (void) strncpy((char *)buf, lbuf, buflen);
572 common2:
573 BUMP(buf, buflen);
574 for (i = 4; i < cnt; i++) {
575 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]);
576 (void) strncpy((char *)buf, lbuf, buflen);
577 BUMP(buf, buflen);
579 break;
583 void
584 krb5_profile_get_options(char *host, char *realm,
585 profile_options_boolean *optionsp)
587 char **realms = NULL;
588 krb5_error_code err = 0;
590 if (!telnet_context) {
591 err = krb5_init_context(&telnet_context);
592 if (err) {
593 (void) fprintf(stderr, gettext(
594 "Error initializing Kerberos 5 library: %s\n"),
595 error_message(err));
596 return;
600 if ((realmdef[1] = realm) == NULL) {
601 err = krb5_get_host_realm(telnet_context, host, &realms);
602 if (err) {
603 (void) fprintf(stderr, gettext(
604 "Error getting Kerberos 5 realms for: %s (%s)\n"),
605 host, error_message(err));
606 return;
608 realmdef[1] = realms[0];
611 profile_get_options_boolean(telnet_context->profile,
612 realmdef, optionsp);
613 profile_get_options_boolean(telnet_context->profile,
614 appdef, optionsp);
617 static void
618 kerberos5_forward(Authenticator *ap)
620 krb5_error_code retval;
621 krb5_ccache ccache;
622 krb5_principal client = 0;
623 krb5_principal server = 0;
624 krb5_data forw_creds;
626 forw_creds.data = 0;
628 if ((retval = krb5_cc_default(telnet_context, &ccache))) {
629 if (auth_debug_mode)
630 (void) printf(gettext(
631 "Kerberos V5: could not get default ccache - %s\r\n"),
632 error_message(retval));
633 return;
636 retval = krb5_cc_get_principal(telnet_context, ccache, &client);
637 if (retval) {
638 if (auth_debug_mode)
639 (void) printf(gettext(
640 "Kerberos V5: could not get default "
641 "principal - %s\r\n"), error_message(retval));
642 goto cleanup;
645 retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
646 "host", KRB5_NT_SRV_HST, &server);
647 if (retval) {
648 if (auth_debug_mode)
649 (void) printf(gettext(
650 "Kerberos V5: could not make server "
651 "principal - %s\r\n"), error_message(retval));
652 goto cleanup;
655 retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net,
656 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
657 if (retval) {
658 if (auth_debug_mode)
659 (void) printf(gettext(
660 "Kerberos V5: could not gen local full "
661 "address - %s\r\n"), error_message(retval));
662 goto cleanup;
665 retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client,
666 server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS,
667 &forw_creds);
668 if (retval) {
669 if (auth_debug_mode)
670 (void) printf(gettext(
671 "Kerberos V5: error getting forwarded "
672 "creds - %s\r\n"), error_message(retval));
673 goto cleanup;
676 /* Send forwarded credentials */
677 if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data,
678 forw_creds.length)) {
679 if (auth_debug_mode)
680 (void) printf(gettext(
681 "Not enough room for authentication data\r\n"));
682 } else if (auth_debug_mode)
683 (void) printf(gettext(
684 "Forwarded local Kerberos V5 credentials to server\r\n"));
685 cleanup:
686 if (client)
687 krb5_free_principal(telnet_context, client);
688 if (server)
689 krb5_free_principal(telnet_context, server);
690 if (forw_creds.data)
691 free(forw_creds.data);
692 /* LINTED */
693 krb5_cc_close(telnet_context, ccache);