xref: /freebsd/crypto/openssh/auth-krb5.c (revision c4f6a2a9e1b1879b618c436ab4f56ff75c73a0f5)
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.8 2002/03/19 10:49:35 markus Exp $");
32 
33 #include "ssh.h"
34 #include "ssh1.h"
35 #include "packet.h"
36 #include "xmalloc.h"
37 #include "log.h"
38 #include "servconf.h"
39 #include "uidswap.h"
40 #include "auth.h"
41 
42 #ifdef KRB5
43 #include <krb5.h>
44 #ifndef HEIMDAL
45 #define krb5_get_err_text(context,code) error_message(code)
46 #endif /* !HEIMDAL */
47 
48 extern ServerOptions	 options;
49 
50 static int
51 krb5_init(void *context)
52 {
53 	Authctxt *authctxt = (Authctxt *)context;
54 	krb5_error_code problem;
55 	static int cleanup_registered = 0;
56 
57 	if (authctxt->krb5_ctx == NULL) {
58 		problem = krb5_init_context(&authctxt->krb5_ctx);
59 		if (problem)
60 			return (problem);
61 		krb5_init_ets(authctxt->krb5_ctx);
62 	}
63 	if (!cleanup_registered) {
64 		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
65 		cleanup_registered = 1;
66 	}
67 	return (0);
68 }
69 
70 /*
71  * Try krb5 authentication. server_user is passed for logging purposes
72  * only, in auth is received ticket, in client is returned principal
73  * from the ticket
74  */
75 int
76 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client)
77 {
78 	krb5_error_code problem;
79 	krb5_principal server;
80 	krb5_data reply;
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 	packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE);
148 	packet_put_string((char *) reply.data, reply.length);
149 	packet_send();
150 	packet_write_wait();
151 
152 	ret = 1;
153  err:
154 	if (server)
155 		krb5_free_principal(authctxt->krb5_ctx, server);
156 	if (ticket)
157 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
158 	if (reply.length)
159 		xfree(reply.data);
160 
161 	if (problem) {
162 		if (authctxt->krb5_ctx != NULL)
163 			debug("Kerberos v5 authentication failed: %s",
164 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
165 		else
166 			debug("Kerberos v5 authentication failed: %d",
167 			    problem);
168 	}
169 
170 	return (ret);
171 }
172 
173 int
174 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
175 {
176 	krb5_error_code problem;
177 	krb5_ccache ccache = NULL;
178 	char *pname;
179 	krb5_creds **creds;
180 
181 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
182 		return (0);
183 
184 	temporarily_use_uid(authctxt->pw);
185 
186 #ifdef HEIMDAL
187 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
188 #else
189 {
190 	char ccname[40];
191 	int tmpfd;
192 
193 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
194 
195 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
196 		log("mkstemp(): %.100s", strerror(errno));
197 		problem = errno;
198 		goto fail;
199 	}
200 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
201 		log("fchmod(): %.100s", strerror(errno));
202 		close(tmpfd);
203 		problem = errno;
204 		goto fail;
205 	}
206 	close(tmpfd);
207 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache);
208 }
209 #endif
210 	if (problem)
211 		goto fail;
212 
213 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
214 	    authctxt->krb5_user);
215 	if (problem)
216 		goto fail;
217 
218 #ifdef HEIMDAL
219 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
220 	    ccache, tgt);
221 	if (problem)
222 		goto fail;
223 #else
224 	problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
225 	    tgt, &creds, NULL);
226 	if (problem)
227 		goto fail;
228 	problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds);
229 	if (problem)
230 		goto fail;
231 #endif
232 
233 	authctxt->krb5_fwd_ccache = ccache;
234 	ccache = NULL;
235 
236 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
237 
238 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
239 	    &pname);
240 	if (problem)
241 		goto fail;
242 
243 	debug("Kerberos v5 TGT accepted (%s)", pname);
244 
245 	restore_uid();
246 
247 	return (1);
248 
249  fail:
250 	if (problem)
251 		debug("Kerberos v5 TGT passing failed: %s",
252 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
253 	if (ccache)
254 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
255 
256 	restore_uid();
257 
258 	return (0);
259 }
260 
261 int
262 auth_krb5_password(Authctxt *authctxt, const char *password)
263 {
264 #ifndef HEIMDAL
265 	krb5_creds creds;
266 	krb5_principal server;
267 	char ccname[40];
268 	int tmpfd;
269 #endif
270 	krb5_error_code problem;
271 
272 	if (authctxt->pw == NULL)
273 		return (0);
274 
275 	temporarily_use_uid(authctxt->pw);
276 
277 	problem = krb5_init(authctxt);
278 	if (problem)
279 		goto out;
280 
281 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
282 		    &authctxt->krb5_user);
283 	if (problem)
284 		goto out;
285 
286 #ifdef HEIMDAL
287 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops,
288 	    &authctxt->krb5_fwd_ccache);
289 	if (problem)
290 		goto out;
291 
292 	problem = krb5_cc_initialize(authctxt->krb5_ctx,
293 	    authctxt->krb5_fwd_ccache, authctxt->krb5_user);
294 	if (problem)
295 		goto out;
296 
297 	restore_uid();
298 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
299 	    authctxt->krb5_fwd_ccache, password, 1, NULL);
300 	temporarily_use_uid(authctxt->pw);
301 
302 	if (problem)
303 		goto out;
304 
305 #else
306 	problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds,
307 	    authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL);
308 	if (problem)
309 		goto out;
310 
311 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
312 	    KRB5_NT_SRV_HST, &server);
313 	if (problem)
314 		goto out;
315 
316 	restore_uid();
317 	problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server,
318 	    NULL, NULL, NULL);
319 	krb5_free_principal(authctxt->krb5_ctx, server);
320 	temporarily_use_uid(authctxt->pw);
321 	if (problem)
322 		goto out;
323 
324 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
325 			  authctxt->pw->pw_name)) {
326 		problem = -1;
327 		goto out;
328 	}
329 
330 	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
331 
332 	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
333 		log("mkstemp(): %.100s", strerror(errno));
334 		problem = errno;
335 		goto out;
336 	}
337 
338 	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
339 		log("fchmod(): %.100s", strerror(errno));
340 		close(tmpfd);
341 		problem = errno;
342 		goto out;
343 	}
344 	close(tmpfd);
345 
346 	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache);
347 	if (problem)
348 		goto out;
349 
350 	problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
351 				     authctxt->krb5_user);
352 	if (problem)
353 		goto out;
354 
355 	problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
356 				 &creds);
357 	if (problem)
358 		goto out;
359 #endif
360 
361 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
362 
363  out:
364 	restore_uid();
365 
366 	if (problem) {
367 		if (authctxt->krb5_ctx != NULL && problem!=-1)
368 			debug("Kerberos password authentication failed: %s",
369 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
370 		else
371 			debug("Kerberos password authentication failed: %d",
372 			    problem);
373 
374 		krb5_cleanup_proc(authctxt);
375 
376 		if (options.kerberos_or_local_passwd)
377 			return (-1);
378 		else
379 			return (0);
380 	}
381 	return (1);
382 }
383 
384 void
385 krb5_cleanup_proc(void *context)
386 {
387 	Authctxt *authctxt = (Authctxt *)context;
388 
389 	debug("krb5_cleanup_proc called");
390 	if (authctxt->krb5_fwd_ccache) {
391 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
392 		authctxt->krb5_fwd_ccache = NULL;
393 	}
394 	if (authctxt->krb5_user) {
395 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
396 		authctxt->krb5_user = NULL;
397 	}
398 	if (authctxt->krb5_auth_ctx) {
399 		krb5_auth_con_free(authctxt->krb5_ctx,
400 		    authctxt->krb5_auth_ctx);
401 		authctxt->krb5_auth_ctx = NULL;
402 	}
403 	if (authctxt->krb5_ctx) {
404 		krb5_free_context(authctxt->krb5_ctx);
405 		authctxt->krb5_ctx = NULL;
406 	}
407 }
408 
409 #endif /* KRB5 */
410