1 /* $NetBSD: k5login.c,v 1.33 2012/04/24 16:52:26 christos Exp $ */
4 * Copyright (c) 1990 The Regents of the University of California.
7 * Redistribution and use in source and binary forms, with or without
8 * 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.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms are permitted
37 * provided that the above copyright notice and this paragraph are
38 * duplicated in all such forms and that any documentation,
39 * advertising materials, and other materials related to such
40 * distribution and use acknowledge that the software was developed
41 * by the University of California, Berkeley. The name of the
42 * University may not be used to endorse or promote products derived
43 * from this software without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
45 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
46 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
49 #include <sys/cdefs.h>
52 static char sccsid
[] = "@(#)klogin.c 5.11 (Berkeley) 7/12/92";
54 __RCSID("$NetBSD: k5login.c,v 1.33 2012/04/24 16:52:26 christos Exp $");
58 #include <sys/param.h>
59 #include <sys/syslog.h>
60 #include <krb5/krb5.h>
69 #define KRB5_DEFAULT_OPTIONS 0
70 #define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */
72 krb5_context kcontext
;
78 extern int login_krb5_forwardable_tgt
;
79 extern int has_ccache
;
81 static char tkt_location
[MAXPATHLEN
];
82 static krb5_creds forw_creds
;
84 static krb5_principal me
;
86 int k5_read_creds(char *);
87 int k5_write_creds(void);
88 int k5_verify_creds(krb5_context
, krb5_ccache
);
89 int k5login(struct passwd
*, char *, char *, char *);
92 static void __printflike(3, 4)
93 k5_log(krb5_context context
, krb5_error_code kerror
, const char *fmt
, ...)
95 const char *msg
= krb5_get_error_message(context
, kerror
);
100 if (vasprintf(&str
, fmt
, ap
) == -1) {
102 syslog(LOG_NOTICE
, "Cannot allocate memory for error %s: %s",
108 syslog(LOG_NOTICE
, "warning: %s: %s", str
, msg
);
109 krb5_free_error_message(kcontext
, msg
);
114 * Verify the Kerberos ticket-granting ticket just retrieved for the
115 * user. If the Kerberos server doesn't respond, assume the user is
116 * trying to fake us out (since we DID just get a TGT from what is
117 * supposedly our KDC). If the host/<host> service is unknown (i.e.,
118 * the local keytab doesn't have it), let her in.
120 * Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
123 k5_verify_creds(krb5_context c
, krb5_ccache ccache
)
125 char phost
[MAXHOSTNAMELEN
];
126 int retval
, have_keys
;
127 krb5_principal princ
;
128 krb5_keyblock
*kb
= 0;
129 krb5_error_code kerror
;
131 krb5_auth_context auth_context
= NULL
;
132 krb5_ticket
*ticket
= NULL
;
134 kerror
= krb5_sname_to_principal(c
, 0, 0, KRB5_NT_SRV_HST
, &princ
);
136 krb5_warn(kcontext
, kerror
, "constructing local service name");
140 /* Do we have host/<host> keys? */
141 /* (use default keytab, kvno IGNORE_VNO to get the first match,
142 * and default enctype.) */
143 kerror
= krb5_kt_read_service_key(c
, NULL
, princ
, 0, 0, &kb
);
145 krb5_free_keyblock(c
, kb
);
146 /* any failure means we don't have keys at all. */
147 have_keys
= kerror
? 0 : 1;
149 /* XXX there should be a krb5 function like mk_req, but taking a full
150 * principal, instead of a service/hostname. (Did I miss one?) */
151 gethostname(phost
, sizeof(phost
));
152 phost
[sizeof(phost
) - 1] = '\0';
154 /* talk to the kdc and construct the ticket */
155 kerror
= krb5_mk_req(c
, &auth_context
, 0, "host", phost
,
157 /* wipe the auth context for rd_req */
159 krb5_auth_con_free(c
, auth_context
);
162 if (kerror
== KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
) {
163 /* we have a service key, so something should be
164 * in the database, therefore this error packet could
165 * have come from an attacker. */
170 /* but if it is unknown and we've got no key, we don't
171 * have any security anyhow, so it is ok. */
178 krb5_warn(kcontext
, kerror
,
179 "Unable to verify Kerberos V5 TGT: %s", phost
);
180 k5_log(kcontext
, kerror
, "Kerberos V5 TGT bad");
184 /* got ticket, try to use it */
185 kerror
= krb5_rd_req(c
, &auth_context
, &packet
,
186 princ
, NULL
, NULL
, &ticket
);
189 /* The krb5 errors aren't specified well, but I think
190 * these values cover the cases we expect. */
192 case ENOENT
: /* no keytab */
193 case KRB5_KT_NOTFOUND
:
197 /* unexpected error: fail */
203 /* we have keys, so if we got any error, we could be
207 krb5_warn(kcontext
, kerror
, "Unable to verify host ticket");
208 k5_log(kcontext
, kerror
, "can't verify v5 ticket (%s)",
209 retval
? "keytab found, assuming failure" :
210 "no keytab found, assuming success");
214 * The host/<host> ticket has been received _and_ verified.
218 /* do cleanup and return */
221 krb5_auth_con_free(c
, auth_context
);
222 krb5_free_principal(c
, princ
);
223 /* possibly ticket and packet need freeing here as well */
228 * Attempt to read forwarded kerberos creds
230 * return 0 on success (forwarded creds in memory)
231 * 1 if no forwarded creds.
234 k5_read_creds(char *username
)
236 krb5_error_code kerror
;
241 memset((char*) &mcreds
, 0, sizeof(forw_creds
));
242 memset((char*) &forw_creds
, 0, sizeof(forw_creds
));
244 kerror
= krb5_cc_default(kcontext
, &ccache
);
246 krb5_warn(kcontext
, kerror
, "while getting default ccache");
250 kerror
= krb5_parse_name(kcontext
, username
, &me
);
252 krb5_warn(kcontext
, kerror
, "when parsing name %s", username
);
257 const char *realm
= krb5_principal_get_realm(kcontext
, me
);
258 size_t rlen
= strlen(realm
);
259 kerror
= krb5_build_principal_ext(kcontext
, &mcreds
.server
,
266 krb5_warn(kcontext
, kerror
, "while building server name");
270 kerror
= krb5_cc_retrieve_cred(kcontext
, ccache
, 0,
271 &mcreds
, &forw_creds
);
273 krb5_warn(kcontext
, kerror
,
274 "while retrieving V5 initial ticket for copy");
280 strlcpy(tkt_location
, getenv("KRB5CCNAME"), sizeof(tkt_location
));
281 krb5tkfile_env
= tkt_location
;
286 krb5_cc_destroy(kcontext
, ccache
);
287 return(!have_forward
);
293 krb5_error_code kerror
;
299 kerror
= krb5_cc_default(kcontext
, &ccache
);
301 krb5_warn(kcontext
, kerror
, "while getting default ccache");
305 kerror
= krb5_cc_initialize(kcontext
, ccache
, me
);
307 krb5_warn(kcontext
, kerror
,
308 "while re-initializing V5 ccache as user");
309 goto nuke_ccache_contents
;
312 kerror
= krb5_cc_store_cred(kcontext
, ccache
, &forw_creds
);
314 krb5_warn(kcontext
, kerror
,
315 "while re-storing V5 ccache as user");
316 goto nuke_ccache_contents
;
319 nuke_ccache_contents
:
320 krb5_free_cred_contents(kcontext
, &forw_creds
);
321 return (kerror
!= 0);
325 * Attempt to log the user in using Kerberos authentication
327 * return 0 on success (will be logged in)
328 * 1 if Kerberos failed (try local password in login)
331 k5login(struct passwd
*pw
, char *instance
, char *localhost
, char *password
)
333 krb5_error_code kerror
;
335 krb5_ccache ccache
= NULL
;
336 char *realm
, *client_name
;
343 * Root logins don't use Kerberos.
344 * If we have a realm, try getting a ticket-granting ticket
345 * and using it to authenticate. Otherwise, return
346 * failure so that we can try the normal passwd file
347 * for a password. If that's ok, log the user in
348 * without issuing any tickets.
350 if (strcmp(pw
->pw_name
, "root") == 0 ||
351 krb5_get_default_realm(kcontext
, &realm
)) {
357 * get TGT for local realm
358 * tickets are stored in a file named TKT_ROOT plus uid
359 * except for user.root tickets.
362 if (strcmp(instance
, "root") != 0)
363 (void)snprintf(tkt_location
, sizeof tkt_location
,
364 "FILE:/tmp/krb5cc_%d", pw
->pw_uid
);
366 (void)snprintf(tkt_location
, sizeof tkt_location
,
367 "FILE:/tmp/krb5cc_root_%d", pw
->pw_uid
);
368 krb5tkfile_env
= tkt_location
;
371 if (strlen(instance
))
372 asprintf(&principal
, "%s/%s", pw
->pw_name
, instance
);
374 principal
= strdup(pw
->pw_name
);
376 syslog(LOG_NOTICE
, "fatal: %s", strerror(errno
));
380 if ((kerror
= krb5_cc_resolve(kcontext
, tkt_location
, &ccache
)) != 0) {
381 k5_log(kcontext
, kerror
, "while getting default ccache");
385 if ((kerror
= krb5_parse_name(kcontext
, principal
, &me
)) != 0) {
386 k5_log(kcontext
, kerror
, "when parsing name %s", principal
);
390 if ((kerror
= krb5_unparse_name(kcontext
, me
, &client_name
)) != 0) {
391 k5_log(kcontext
, kerror
, "when unparsing name %s", principal
);
395 kerror
= krb5_cc_initialize(kcontext
, ccache
, me
);
397 k5_log(kcontext
, kerror
, "when initializing cache %s",
402 memset(&my_creds
, 0, sizeof(my_creds
));
403 krb5_get_init_creds_opt
*opt
;
405 if ((kerror
= krb5_get_init_creds_opt_alloc(kcontext
, &opt
)) != 0) {
406 k5_log(kcontext
, kerror
, "while getting options");
409 if (login_krb5_forwardable_tgt
)
410 krb5_get_init_creds_opt_set_forwardable(opt
, 1);
412 kerror
= krb5_get_init_creds_password(kcontext
, &my_creds
, me
, password
,
413 NULL
, NULL
, 0, NULL
, opt
);
415 krb5_get_init_creds_opt_free(kcontext
, opt
);
417 kerror
= krb5_cc_store_cred(kcontext
, ccache
, &my_creds
);
419 if (chown(&tkt_location
[5], pw
->pw_uid
, pw
->pw_gid
) < 0)
420 syslog(LOG_ERR
, "chown tkfile (%s): %m", &tkt_location
[5]);
423 if (kerror
== KRB5KRB_AP_ERR_BAD_INTEGRITY
)
424 printf("%s: Kerberos Password incorrect\n", principal
);
426 krb5_warn(kcontext
, kerror
,
427 "while getting initial credentials");
432 if (k5_verify_creds(kcontext
, ccache
) < 0)
441 * Remove any credentials
446 krb5_error_code kerror
;
447 krb5_ccache ccache
= NULL
;
449 if (krb5tkfile_env
== NULL
)
452 kerror
= krb5_cc_resolve(kcontext
, krb5tkfile_env
, &ccache
);
454 (void)krb5_cc_destroy(kcontext
, ccache
);
456 #endif /* KERBEROS5 */