xref: /freebsd/crypto/openssh/auth-krb5.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*
2  *    Kerberos v5 authentication and ticket-passing routines.
3  *
4  * $xFreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp$
5  */
6 /*
7  * Copyright (c) 2002 Daniel Kouril.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "includes.h"
31 RCSID("$OpenBSD: auth-krb5.c,v 1.10 2002/11/21 23:03:51 deraadt Exp $");
32 RCSID("$FreeBSD$");
33 
34 #include "ssh.h"
35 #include "ssh1.h"
36 #include "packet.h"
37 #include "xmalloc.h"
38 #include "log.h"
39 #include "servconf.h"
40 #include "uidswap.h"
41 #include "auth.h"
42 
43 #ifdef KRB5
44 #include <krb5.h>
45 #ifndef HEIMDAL
46 #define krb5_get_err_text(context,code) error_message(code)
47 #endif /* !HEIMDAL */
48 
49 extern ServerOptions	 options;
50 
51 static int
52 krb5_init(void *context)
53 {
54 	Authctxt *authctxt = (Authctxt *)context;
55 	krb5_error_code problem;
56 	static int cleanup_registered = 0;
57 
58 	if (authctxt->krb5_ctx == NULL) {
59 		problem = krb5_init_context(&authctxt->krb5_ctx);
60 		if (problem)
61 			return (problem);
62 		krb5_init_ets(authctxt->krb5_ctx);
63 	}
64 	if (!cleanup_registered) {
65 		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
66 		cleanup_registered = 1;
67 	}
68 	return (0);
69 }
70 
71 /*
72  * Try krb5 authentication. server_user is passed for logging purposes
73  * only, in auth is received ticket, in client is returned principal
74  * from the ticket
75  */
76 int
77 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply)
78 {
79 	krb5_error_code problem;
80 	krb5_principal server;
81 	krb5_ticket *ticket;
82 	int fd, ret;
83 
84 	ret = 0;
85 	server = NULL;
86 	ticket = NULL;
87 	reply->length = 0;
88 
89 	problem = krb5_init(authctxt);
90 	if (problem)
91 		goto err;
92 
93 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
94 	    &authctxt->krb5_auth_ctx);
95 	if (problem)
96 		goto err;
97 
98 	fd = packet_get_connection_in();
99 #ifdef HEIMDAL
100 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
101 	    authctxt->krb5_auth_ctx, &fd);
102 #else
103 	problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx,
104 	    authctxt->krb5_auth_ctx,fd,
105 	    KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR |
106 	    KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
107 #endif
108 	if (problem)
109 		goto err;
110 
111 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
112 	    KRB5_NT_SRV_HST, &server);
113 	if (problem)
114 		goto err;
115 
116 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
117 	    auth, server, NULL, NULL, &ticket);
118 	if (problem)
119 		goto err;
120 
121 #ifdef HEIMDAL
122 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
123 	    &authctxt->krb5_user);
124 #else
125 	problem = krb5_copy_principal(authctxt->krb5_ctx,
126 				      ticket->enc_part2->client,
127 				      &authctxt->krb5_user);
128 #endif
129 	if (problem)
130 		goto err;
131 
132 	/* if client wants mutual auth */
133 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
134 	    reply);
135 	if (problem)
136 		goto err;
137 
138 	/* Check .k5login authorization now. */
139 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
140 	    authctxt->pw->pw_name))
141 		goto err;
142 
143 	if (client)
144 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
145 		    client);
146 
147 	ret = 1;
148  err:
149 	if (server)
150 		krb5_free_principal(authctxt->krb5_ctx, server);
151 	if (ticket)
152 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
153 	if (!ret && reply->length) {
154 		xfree(reply->data);
155 		memset(reply, 0, sizeof(*reply));
156 	}
157 
158 	if (problem) {
159 		if (authctxt->krb5_ctx != NULL)
160 			debug("Kerberos v5 authentication failed: %s",
161 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
162 		else
163 			debug("Kerberos v5 authentication failed: %d",
164 			    problem);
165 	}
166 
167 	return (ret);
168 }
169 
170 int
171 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
172 {
173 	krb5_error_code problem;
174 	krb5_ccache ccache = NULL;
175 	char *pname;
176 	krb5_creds **creds;
177 
178 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
179 		return (0);
180 
181 	temporarily_use_uid(authctxt->pw);
182 
183 #ifdef HEIMDAL
184 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
185 #else
186 {
187 	char ccname[40];
188 	int tmpfd;
189 
190 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
191 
192 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
193 		log("mkstemp(): %.100s", strerror(errno));
194 		problem = errno;
195 		goto fail;
196 	}
197 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
198 		log("fchmod(): %.100s", strerror(errno));
199 		close(tmpfd);
200 		problem = errno;
201 		goto fail;
202 	}
203 	close(tmpfd);
204 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache);
205 }
206 #endif
207 	if (problem)
208 		goto fail;
209 
210 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
211 	    authctxt->krb5_user);
212 	if (problem)
213 		goto fail;
214 
215 #ifdef HEIMDAL
216 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
217 	    ccache, tgt);
218 	if (problem)
219 		goto fail;
220 #else
221 	problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
222 	    tgt, &creds, NULL);
223 	if (problem)
224 		goto fail;
225 	problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds);
226 	if (problem)
227 		goto fail;
228 #endif
229 
230 	authctxt->krb5_fwd_ccache = ccache;
231 	ccache = NULL;
232 
233 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
234 
235 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
236 	    &pname);
237 	if (problem)
238 		goto fail;
239 
240 	debug("Kerberos v5 TGT accepted (%s)", pname);
241 
242 	restore_uid();
243 
244 	return (1);
245 
246  fail:
247 	if (problem)
248 		debug("Kerberos v5 TGT passing failed: %s",
249 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
250 	if (ccache)
251 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
252 
253 	restore_uid();
254 
255 	return (0);
256 }
257 
258 int
259 auth_krb5_password(Authctxt *authctxt, const char *password)
260 {
261 #ifndef HEIMDAL
262 	krb5_creds creds;
263 	krb5_principal server;
264 	char ccname[40];
265 	int tmpfd;
266 #endif
267 	krb5_error_code problem;
268 
269 	if (authctxt->pw == NULL)
270 		return (0);
271 
272 	temporarily_use_uid(authctxt->pw);
273 
274 	problem = krb5_init(authctxt);
275 	if (problem)
276 		goto out;
277 
278 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
279 		    &authctxt->krb5_user);
280 	if (problem)
281 		goto out;
282 
283 #ifdef HEIMDAL
284 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops,
285 	    &authctxt->krb5_fwd_ccache);
286 	if (problem)
287 		goto out;
288 
289 	problem = krb5_cc_initialize(authctxt->krb5_ctx,
290 	    authctxt->krb5_fwd_ccache, authctxt->krb5_user);
291 	if (problem)
292 		goto out;
293 
294 	restore_uid();
295 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
296 	    authctxt->krb5_fwd_ccache, password, 1, NULL);
297 	temporarily_use_uid(authctxt->pw);
298 
299 	if (problem)
300 		goto out;
301 
302 #else
303 	problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds,
304 	    authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL);
305 	if (problem)
306 		goto out;
307 
308 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
309 	    KRB5_NT_SRV_HST, &server);
310 	if (problem)
311 		goto out;
312 
313 	restore_uid();
314 	problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server,
315 	    NULL, NULL, NULL);
316 	krb5_free_principal(authctxt->krb5_ctx, server);
317 	temporarily_use_uid(authctxt->pw);
318 	if (problem)
319 		goto out;
320 
321 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
322 			  authctxt->pw->pw_name)) {
323 		problem = -1;
324 		goto out;
325 	}
326 
327 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
328 
329 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
330 		log("mkstemp(): %.100s", strerror(errno));
331 		problem = errno;
332 		goto out;
333 	}
334 
335 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
336 		log("fchmod(): %.100s", strerror(errno));
337 		close(tmpfd);
338 		problem = errno;
339 		goto out;
340 	}
341 	close(tmpfd);
342 
343 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache);
344 	if (problem)
345 		goto out;
346 
347 	problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
348 				     authctxt->krb5_user);
349 	if (problem)
350 		goto out;
351 
352 	problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
353 				 &creds);
354 	if (problem)
355 		goto out;
356 #endif
357 
358 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
359 
360  out:
361 	restore_uid();
362 
363 	if (problem) {
364 		if (authctxt->krb5_ctx != NULL && problem!=-1)
365 			debug("Kerberos password authentication failed: %s",
366 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
367 		else
368 			debug("Kerberos password authentication failed: %d",
369 			    problem);
370 
371 		krb5_cleanup_proc(authctxt);
372 
373 		if (options.kerberos_or_local_passwd)
374 			return (-1);
375 		else
376 			return (0);
377 	}
378 	return (1);
379 }
380 
381 void
382 krb5_cleanup_proc(void *context)
383 {
384 	Authctxt *authctxt = (Authctxt *)context;
385 
386 	debug("krb5_cleanup_proc called");
387 	if (authctxt->krb5_fwd_ccache) {
388 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
389 		authctxt->krb5_fwd_ccache = NULL;
390 	}
391 	if (authctxt->krb5_user) {
392 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
393 		authctxt->krb5_user = NULL;
394 	}
395 	if (authctxt->krb5_auth_ctx) {
396 		krb5_auth_con_free(authctxt->krb5_ctx,
397 		    authctxt->krb5_auth_ctx);
398 		authctxt->krb5_auth_ctx = NULL;
399 	}
400 	if (authctxt->krb5_ctx) {
401 		krb5_free_context(authctxt->krb5_ctx);
402 		authctxt->krb5_ctx = NULL;
403 	}
404 }
405 
406 #endif /* KRB5 */
407