dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / usr.bin / telnet / kerberos5.c
blobffcdf021839e4990d3fb2cf461694db9b583eff9
1 /*
2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * usr/src/cmd/cmd-inet/usr.bin/telnet/kerberos5.c
9 * Copyright (c) 1991, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
41 /* based on @(#)kerberos5.c 8.1 (Berkeley) 6/4/93 */
44 * Copyright (C) 1990 by the Massachusetts Institute of Technology
46 * Export of this software from the United States of America may
47 * require a specific license from the United States Government.
48 * It is the responsibility of any person or organization contemplating
49 * export to obtain such a license before exporting.
51 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
52 * distribute this software and its documentation for any purpose and
53 * without fee is hereby granted, provided that the above copyright
54 * notice appear in all copies and that both that copyright notice and
55 * this permission notice appear in supporting documentation, and that
56 * the name of M.I.T. not be used in advertising or publicity pertaining
57 * to distribution of the software without specific, written prior
58 * permission. Furthermore if you modify this software you must label
59 * your software as modified software and not distribute it in such a
60 * fashion that it might be confused with the original M.I.T. software.
61 * M.I.T. makes no representations about the suitability of
62 * this software for any purpose. It is provided "as is" without express
63 * or implied warranty.
67 #include <arpa/telnet.h>
68 #include <stdio.h>
69 #include <ctype.h>
70 #include <syslog.h>
71 #include <stdlib.h>
73 /* the following are from the kerberos tree */
74 #include <k5-int.h>
75 #include <com_err.h>
76 #include <netdb.h>
77 #include <profile/prof_int.h>
78 #include <sys/param.h>
79 #include "externs.h"
81 extern char *RemoteHostName;
82 extern boolean_t auth_debug_mode;
83 extern int net;
85 #define ACCEPTED_ENCTYPE(a) \
86 (a == ENCTYPE_DES_CBC_CRC || a == ENCTYPE_DES_CBC_MD5)
87 /* for comapatibility with non-Solaris KDC's, this has to be big enough */
88 #define KERBEROS_BUFSIZ 8192
90 int forward_flags = 0; /* Flags get set in telnet/main.c on -f and -F */
91 static void kerberos5_forward(Authenticator *);
93 static unsigned char str_data[KERBEROS_BUFSIZ] = { IAC, SB,
94 TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, };
95 static char *appdef[] = { "appdefaults", "telnet", NULL };
96 static char *realmdef[] = { "realms", NULL, "telnet", NULL };
98 static krb5_auth_context auth_context = 0;
100 static krb5_data auth; /* telnetd gets session key from here */
101 static krb5_ticket *ticket = NULL;
102 /* telnet matches the AP_REQ and AP_REP with this */
104 static krb5_keyblock *session_key = 0;
105 char *telnet_krb5_realm = NULL;
108 * Change the kerberos realm
110 void
111 set_krb5_realm(char *name)
113 if (name == NULL) {
114 (void) fprintf(stderr, gettext("Could not set Kerberos realm, "
115 "no realm provided.\n"));
116 return;
119 free(telnet_krb5_realm);
121 telnet_krb5_realm = (char *)strdup(name);
123 if (telnet_krb5_realm == NULL)
124 (void) fprintf(stderr, gettext(
125 "Could not set Kerberos realm, malloc failed\n"));
128 #define RETURN_NOMEM { errno = ENOMEM; return (-1); }
130 static int
131 krb5_send_data(Authenticator *ap, int type, krb5_pointer d, int c)
133 /* the first 3 bytes are control chars */
134 unsigned char *p = str_data + 4;
135 unsigned char *cd = (unsigned char *)d;
136 /* spaceleft is incremented whenever p is decremented */
137 size_t spaceleft = sizeof (str_data) - 4;
139 if (c == -1)
140 c = strlen((char *)cd);
142 if (auth_debug_mode) {
143 (void) printf("%s:%d: [%d] (%d)",
144 str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY",
145 str_data[3], type, c);
146 printd(d, c);
147 (void) printf("\r\n");
150 if (spaceleft < 3)
151 RETURN_NOMEM;
152 *p++ = ap->type;
153 *p++ = ap->way;
154 *p++ = type;
155 spaceleft -= 3;
157 while (c-- > 0) {
158 if (spaceleft < 2)
159 RETURN_NOMEM;
160 if ((*p++ = *cd++) == IAC) {
161 *p++ = IAC;
162 spaceleft -= 2;
166 if (spaceleft < 2)
167 RETURN_NOMEM;
168 *p++ = IAC;
169 *p++ = SE;
170 if (str_data[3] == TELQUAL_IS)
171 printsub('>', &str_data[2], p - &str_data[2]);
172 return (net_write(str_data, p - str_data));
175 krb5_context telnet_context = 0;
177 /* ARGSUSED */
179 kerberos5_init(Authenticator *ap)
181 krb5_error_code retval;
183 str_data[3] = TELQUAL_IS;
184 if (krb5auth_flag && (telnet_context == 0)) {
185 retval = krb5_init_context(&telnet_context);
186 if (retval)
187 return (0);
189 return (1);
193 kerberos5_send(Authenticator *ap)
195 krb5_error_code retval;
196 krb5_ccache ccache;
197 krb5_creds creds; /* telnet gets session key from here */
198 krb5_creds *new_creds = 0;
199 int ap_opts;
200 char type_check[2];
201 krb5_data check_data;
203 krb5_keyblock *newkey = 0;
205 int i;
206 krb5_enctype *ktypes;
208 if (!UserNameRequested) {
209 if (auth_debug_mode)
210 (void) printf(gettext("telnet: Kerberos V5: "
211 "no user name supplied\r\n"));
212 return (0);
215 if ((retval = krb5_cc_default(telnet_context, &ccache))) {
216 if (auth_debug_mode)
217 (void) printf(gettext("telnet: Kerberos V5: "
218 "could not get default ccache\r\n"));
219 return (0);
222 (void) memset((char *)&creds, 0, sizeof (creds));
223 if (auth_debug_mode)
224 printf("telnet: calling krb5_sname_to_principal\n");
225 if ((retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
226 "host", KRB5_NT_SRV_HST, &creds.server))) {
227 if (auth_debug_mode)
228 (void) printf(gettext("telnet: Kerberos V5: error "
229 "while constructing service name: %s\r\n"),
230 error_message(retval));
231 return (0);
233 if (auth_debug_mode)
234 printf("telnet: done calling krb5_sname_to_principal\n");
236 if (telnet_krb5_realm != NULL) {
237 krb5_data rdata;
239 rdata.magic = 0;
240 rdata.length = strlen(telnet_krb5_realm);
241 rdata.data = (char *)malloc(rdata.length + 1);
242 if (rdata.data == NULL) {
243 (void) fprintf(stderr, gettext("malloc failed\n"));
244 return (0);
246 (void) strcpy(rdata.data, telnet_krb5_realm);
247 krb5_princ_set_realm(telnet_context, creds.server, &rdata);
248 if (auth_debug_mode)
249 (void) printf(gettext(
250 "telnet: Kerberos V5: set kerberos realm to %s\r\n"),
251 telnet_krb5_realm);
254 if ((retval = krb5_cc_get_principal(telnet_context, ccache,
255 &creds.client)) != 0) {
256 if (auth_debug_mode) {
257 (void) printf(gettext(
258 "telnet: Kerberos V5: failure on principal "
259 "(%s)\r\n"), error_message(retval));
261 krb5_free_cred_contents(telnet_context, &creds);
262 return (0);
265 * Check to to confirm that at least one of the supported
266 * encryption types (des-cbc-md5, des-cbc-crc is available. If
267 * one is available then use it to obtain credentials.
270 if ((retval = krb5_get_tgs_ktypes(telnet_context, creds.server,
271 &ktypes))) {
272 if (auth_debug_mode) {
273 (void) printf(gettext(
274 "telnet: Kerberos V5: could not determine "
275 "TGS encryption types "
276 "(see default_tgs_enctypes in krb5.conf) "
277 "(%s)\r\n"), error_message(retval));
279 krb5_free_cred_contents(telnet_context, &creds);
280 return (0);
283 for (i = 0; ktypes[i]; i++) {
284 if (ACCEPTED_ENCTYPE(ktypes[i]))
285 break;
288 if (ktypes[i] == 0) {
289 if (auth_debug_mode) {
290 (void) printf(gettext(
291 "telnet: Kerberos V5: "
292 "failure on encryption types. "
293 "Cannot find des-cbc-md5 or des-cbc-crc "
294 "in list of TGS encryption types "
295 "(see default_tgs_enctypes in krb5.conf)\n"));
297 krb5_free_cred_contents(telnet_context, &creds);
298 return (0);
301 creds.keyblock.enctype = ktypes[i];
302 if ((retval = krb5_get_credentials(telnet_context, 0,
303 ccache, &creds, &new_creds))) {
304 if (auth_debug_mode) {
305 (void) printf(gettext(
306 "telnet: Kerberos V5: failure on credentials "
307 "(%s)\r\n"), error_message(retval));
309 krb5_free_cred_contents(telnet_context, &creds);
310 return (0);
313 ap_opts = ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
314 AP_OPTS_MUTUAL_REQUIRED : 0;
316 ap_opts |= AP_OPTS_USE_SUBKEY;
318 if (auth_context) {
319 krb5_auth_con_free(telnet_context, auth_context);
320 auth_context = 0;
322 if ((retval = krb5_auth_con_init(telnet_context, &auth_context))) {
323 if (auth_debug_mode) {
324 (void) printf(gettext(
325 "Kerberos V5: failed to init auth_context "
326 "(%s)\r\n"), error_message(retval));
328 return (0);
331 krb5_auth_con_setflags(telnet_context, auth_context,
332 KRB5_AUTH_CONTEXT_RET_TIME);
334 type_check[0] = ap->type;
335 type_check[1] = ap->way;
336 check_data.magic = KV5M_DATA;
337 check_data.length = 2;
338 check_data.data = (char *)&type_check;
340 retval = krb5_mk_req_extended(telnet_context, &auth_context, ap_opts,
341 &check_data, new_creds, &auth);
343 krb5_auth_con_getlocalsubkey(telnet_context, auth_context, &newkey);
344 if (session_key) {
345 krb5_free_keyblock(telnet_context, session_key);
346 session_key = 0;
349 if (newkey) {
351 * keep the key in our private storage, but don't use it
352 * yet---see kerberos5_reply() below
354 if (!(ACCEPTED_ENCTYPE(newkey->enctype))) {
355 if (!(ACCEPTED_ENCTYPE(new_creds->keyblock.enctype)))
356 /* use the session key in credentials instead */
357 krb5_copy_keyblock(telnet_context,
358 &new_creds->keyblock, &session_key);
359 } else
360 krb5_copy_keyblock(telnet_context,
361 newkey, &session_key);
363 krb5_free_keyblock(telnet_context, newkey);
366 krb5_free_cred_contents(telnet_context, &creds);
367 krb5_free_creds(telnet_context, new_creds);
369 if (retval) {
370 if (auth_debug_mode)
371 (void) printf(gettext(
372 "telnet: Kerberos V5: mk_req failed (%s)\r\n"),
373 error_message(retval));
374 return (0);
377 if ((auth_sendname((uchar_t *)UserNameRequested,
378 strlen(UserNameRequested))) == 0) {
379 if (auth_debug_mode)
380 (void) printf(gettext(
381 "telnet: Not enough room for user name\r\n"));
382 return (0);
384 retval = krb5_send_data(ap, KRB_AUTH, auth.data, auth.length);
385 if (auth_debug_mode && retval) {
386 (void) printf(gettext(
387 "telnet: Sent Kerberos V5 credentials to server\r\n"));
388 } else if (auth_debug_mode) {
389 (void) printf(gettext(
390 "telnet: Not enough room for authentication data\r\n"));
391 return (0);
393 return (1);
396 void
397 kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt)
399 Session_Key skey;
400 static boolean_t mutual_complete = B_FALSE;
402 if (cnt-- < 1)
403 return;
404 switch (*data++) {
405 case KRB_REJECT:
406 if (cnt > 0)
407 (void) printf(gettext(
408 "[ Kerberos V5 refuses authentication because "
409 "%.*s ]\r\n"), cnt, data);
410 else
411 (void) printf(gettext(
412 "[ Kerberos V5 refuses authentication ]\r\n"));
413 auth_send_retry();
414 return;
415 case KRB_ACCEPT:
416 if (!mutual_complete) {
417 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
418 (void) printf(gettext(
419 "[ Kerberos V5 accepted you, but didn't "
420 "provide mutual authentication! ]\r\n"));
421 auth_send_retry();
422 return;
425 if (session_key) {
426 skey.type = SK_DES;
427 skey.length = 8;
428 skey.data = session_key->contents;
429 encrypt_session_key(&skey);
432 if (cnt)
433 (void) printf(gettext(
434 "[ Kerberos V5 accepts you as ``%.*s'' ]\r\n"),
435 cnt, data);
436 else
437 (void) printf(gettext(
438 "[ Kerberos V5 accepts you ]\r\n"));
439 auth_finished(ap, AUTH_USER);
441 if (forward_flags & OPTS_FORWARD_CREDS)
442 kerberos5_forward(ap);
444 break;
445 case KRB_RESPONSE:
446 if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
447 /* the rest of the reply should contain a krb_ap_rep */
448 krb5_ap_rep_enc_part *reply;
449 krb5_data inbuf;
450 krb5_error_code retval;
452 inbuf.length = cnt;
453 inbuf.data = (char *)data;
455 retval = krb5_rd_rep(telnet_context, auth_context,
456 &inbuf, &reply);
457 if (retval) {
458 (void) printf(gettext(
459 "[ Mutual authentication failed: "
460 "%s ]\r\n"), error_message(retval));
461 auth_send_retry();
462 return;
464 krb5_free_ap_rep_enc_part(telnet_context, reply);
466 if (session_key) {
467 skey.type = SK_DES;
468 skey.length = 8;
469 skey.data = session_key->contents;
470 encrypt_session_key(&skey);
472 mutual_complete = B_TRUE;
474 return;
475 case KRB_FORWARD_ACCEPT:
476 (void) printf(gettext(
477 "[ Kerberos V5 accepted forwarded credentials ]\r\n"));
478 return;
479 case KRB_FORWARD_REJECT:
480 (void) printf(gettext(
481 "[ Kerberos V5 refuses forwarded credentials because "
482 "%.*s ]\r\n"), cnt, data);
483 return;
484 default:
485 if (auth_debug_mode)
486 (void) printf(gettext(
487 "Unknown Kerberos option %d\r\n"), data[-1]);
488 return;
492 /* ARGSUSED */
494 kerberos5_status(Authenticator *ap, char *name, int level)
496 if (level < AUTH_USER)
497 return (level);
499 if (UserNameRequested && krb5_kuserok(telnet_context,
500 ticket->enc_part2->client, UserNameRequested)) {
502 /* the name buffer comes from telnetd/telnetd{-ktd}.c */
503 (void) strncpy(name, UserNameRequested, MAXNAMELEN);
504 name[MAXNAMELEN-1] = '\0';
505 return (AUTH_VALID);
506 } else
507 return (AUTH_USER);
510 #define BUMP(buf, len) while (*(buf)) {++(buf), --(len); }
511 #define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len); }
514 * Used with the set opt command to print suboptions
516 void
517 kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
519 char lbuf[AUTH_LBUF_BUFSIZ];
520 register int i;
522 buf[buflen-1] = '\0'; /* make sure its NULL terminated */
523 buflen -= 1;
525 switch (data[3]) {
526 case KRB_REJECT: /* Rejected (reason might follow) */
527 (void) strncpy((char *)buf, " REJECT ", buflen);
528 goto common;
530 case KRB_ACCEPT: /* Accepted (name might follow) */
531 (void) strncpy((char *)buf, " ACCEPT ", buflen);
532 common:
533 BUMP(buf, buflen);
534 if (cnt <= 4)
535 break;
536 ADDC(buf, buflen, '"');
537 for (i = 4; i < cnt; i++)
538 ADDC(buf, buflen, data[i]);
539 ADDC(buf, buflen, '"');
540 ADDC(buf, buflen, '\0');
541 break;
543 case KRB_AUTH: /* Authentication data follows */
544 (void) strncpy((char *)buf, " AUTH", buflen);
545 goto common2;
547 case KRB_RESPONSE:
548 (void) strncpy((char *)buf, " RESPONSE", buflen);
549 goto common2;
551 case KRB_FORWARD: /* Forwarded credentials follow */
552 (void) strncpy((char *)buf, " FORWARD", buflen);
553 goto common2;
555 case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */
556 (void) strncpy((char *)buf, " FORWARD_ACCEPT", buflen);
557 goto common2;
559 case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */
560 /* (reason might follow) */
561 (void) strncpy((char *)buf, " FORWARD_REJECT", buflen);
562 goto common2;
564 default:
565 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ,
566 gettext(" %d (unknown)"),
567 data[3]);
568 (void) strncpy((char *)buf, lbuf, buflen);
569 common2:
570 BUMP(buf, buflen);
571 for (i = 4; i < cnt; i++) {
572 (void) snprintf(lbuf, AUTH_LBUF_BUFSIZ, " %d", data[i]);
573 (void) strncpy((char *)buf, lbuf, buflen);
574 BUMP(buf, buflen);
576 break;
580 void
581 krb5_profile_get_options(char *host, char *realm,
582 profile_options_boolean *optionsp)
584 char **realms = NULL;
585 krb5_error_code err = 0;
587 if (!telnet_context) {
588 err = krb5_init_context(&telnet_context);
589 if (err) {
590 (void) fprintf(stderr, gettext(
591 "Error initializing Kerberos 5 library: %s\n"),
592 error_message(err));
593 return;
597 if ((realmdef[1] = realm) == NULL) {
598 err = krb5_get_host_realm(telnet_context, host, &realms);
599 if (err) {
600 (void) fprintf(stderr, gettext(
601 "Error getting Kerberos 5 realms for: %s (%s)\n"),
602 host, error_message(err));
603 return;
605 realmdef[1] = realms[0];
608 profile_get_options_boolean(telnet_context->profile,
609 realmdef, optionsp);
610 profile_get_options_boolean(telnet_context->profile,
611 appdef, optionsp);
614 static void
615 kerberos5_forward(Authenticator *ap)
617 krb5_error_code retval;
618 krb5_ccache ccache;
619 krb5_principal client = 0;
620 krb5_principal server = 0;
621 krb5_data forw_creds;
623 forw_creds.data = 0;
625 if ((retval = krb5_cc_default(telnet_context, &ccache))) {
626 if (auth_debug_mode)
627 (void) printf(gettext(
628 "Kerberos V5: could not get default ccache - %s\r\n"),
629 error_message(retval));
630 return;
633 retval = krb5_cc_get_principal(telnet_context, ccache, &client);
634 if (retval) {
635 if (auth_debug_mode)
636 (void) printf(gettext(
637 "Kerberos V5: could not get default "
638 "principal - %s\r\n"), error_message(retval));
639 goto cleanup;
642 retval = krb5_sname_to_principal(telnet_context, RemoteHostName,
643 "host", KRB5_NT_SRV_HST, &server);
644 if (retval) {
645 if (auth_debug_mode)
646 (void) printf(gettext(
647 "Kerberos V5: could not make server "
648 "principal - %s\r\n"), error_message(retval));
649 goto cleanup;
652 retval = krb5_auth_con_genaddrs(telnet_context, auth_context, net,
653 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
654 if (retval) {
655 if (auth_debug_mode)
656 (void) printf(gettext(
657 "Kerberos V5: could not gen local full "
658 "address - %s\r\n"), error_message(retval));
659 goto cleanup;
662 retval = krb5_fwd_tgt_creds(telnet_context, auth_context, 0, client,
663 server, ccache, forward_flags & OPTS_FORWARDABLE_CREDS,
664 &forw_creds);
665 if (retval) {
666 if (auth_debug_mode)
667 (void) printf(gettext(
668 "Kerberos V5: error getting forwarded "
669 "creds - %s\r\n"), error_message(retval));
670 goto cleanup;
673 /* Send forwarded credentials */
674 if (!krb5_send_data(ap, KRB_FORWARD, forw_creds.data,
675 forw_creds.length)) {
676 if (auth_debug_mode)
677 (void) printf(gettext(
678 "Not enough room for authentication data\r\n"));
679 } else if (auth_debug_mode)
680 (void) printf(gettext(
681 "Forwarded local Kerberos V5 credentials to server\r\n"));
682 cleanup:
683 if (client)
684 krb5_free_principal(telnet_context, client);
685 if (server)
686 krb5_free_principal(telnet_context, server);
687 free(forw_creds.data);
688 /* LINTED */
689 krb5_cc_close(telnet_context, ccache);