xref: /netbsd-src/crypto/external/bsd/openssh/dist/auth-krb5.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: auth-krb5.c,v 1.5 2013/11/08 19:18:24 christos Exp $	*/
2 /* $OpenBSD: auth-krb5.c,v 1.20 2013/07/20 01:55:13 djm Exp $ */
3 /*
4  *    Kerberos v5 authentication and ticket-passing routines.
5  *
6  * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $
7  */
8 /*
9  * Copyright (c) 2002 Daniel Kouril.  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "includes.h"
33 __RCSID("$NetBSD: auth-krb5.c,v 1.5 2013/11/08 19:18:24 christos Exp $");
34 #include <sys/types.h>
35 #include <pwd.h>
36 #include <stdarg.h>
37 #include <string.h>
38 
39 #include "xmalloc.h"
40 #include "ssh.h"
41 #include "ssh1.h"
42 #include "packet.h"
43 #include "log.h"
44 #include "buffer.h"
45 #include "servconf.h"
46 #include "uidswap.h"
47 #include "key.h"
48 #include "hostfile.h"
49 #include "auth.h"
50 
51 #ifdef KRB5
52 #include <krb5.h>
53 
54 extern ServerOptions	 options;
55 
56 static int
57 krb5_init(void *context)
58 {
59 	Authctxt *authctxt = (Authctxt *)context;
60 	krb5_error_code problem;
61 
62 	if (authctxt->krb5_ctx == NULL) {
63 		problem = krb5_init_context(&authctxt->krb5_ctx);
64 		if (problem)
65 			return (problem);
66 		krb5_init_ets(authctxt->krb5_ctx);
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 	const char *errtxt;
84 
85 	ret = 0;
86 	server = NULL;
87 	ticket = NULL;
88 	reply->length = 0;
89 
90 	problem = krb5_init(authctxt);
91 	if (problem)
92 		goto err;
93 
94 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
95 	    &authctxt->krb5_auth_ctx);
96 	if (problem)
97 		goto err;
98 
99 	fd = packet_get_connection_in();
100 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
101 	    authctxt->krb5_auth_ctx, &fd);
102 	if (problem)
103 		goto err;
104 
105 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
106 	    KRB5_NT_SRV_HST, &server);
107 	if (problem)
108 		goto err;
109 
110 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
111 	    auth, server, NULL, NULL, &ticket);
112 	if (problem)
113 		goto err;
114 
115 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
116 	    &authctxt->krb5_user);
117 	if (problem)
118 		goto err;
119 
120 	/* if client wants mutual auth */
121 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
122 	    reply);
123 	if (problem)
124 		goto err;
125 
126 	/* Check .k5login authorization now. */
127 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
128 	    authctxt->pw->pw_name))
129 		goto err;
130 
131 	if (client)
132 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
133 		    client);
134 
135 	ret = 1;
136  err:
137 	if (server)
138 		krb5_free_principal(authctxt->krb5_ctx, server);
139 	if (ticket)
140 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
141 	if (!ret && reply->length) {
142 		free(reply->data);
143 		memset(reply, 0, sizeof(*reply));
144 	}
145 
146 	if (problem) {
147 		errtxt = NULL;
148 		if (authctxt->krb5_ctx != NULL)
149 			errtxt = krb5_get_error_message(authctxt->krb5_ctx,
150 			    problem);
151 		if (errtxt != NULL) {
152 			debug("Kerberos v5 authentication failed: %s", errtxt);
153 			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
154 		} else
155 			debug("Kerberos v5 authentication failed: %d",
156 			    problem);
157 	}
158 
159 	return (ret);
160 }
161 
162 int
163 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
164 {
165 	krb5_error_code problem;
166 	krb5_ccache ccache = NULL;
167 	char *pname;
168 	const char *errtxt;
169 
170 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
171 		return (0);
172 
173 	temporarily_use_uid(authctxt->pw);
174 
175 	problem = krb5_cc_new_unique(authctxt->krb5_ctx, "FILE", NULL, &ccache);
176 	if (problem)
177 		goto fail;
178 
179 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
180 	    authctxt->krb5_user);
181 	if (problem)
182 		goto fail;
183 
184 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
185 	    ccache, tgt);
186 	if (problem)
187 		goto fail;
188 
189 	authctxt->krb5_fwd_ccache = ccache;
190 	ccache = NULL;
191 
192 	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
193 
194 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
195 	    &pname);
196 	if (problem)
197 		goto fail;
198 
199 #ifdef USE_PAM
200 	if (options.use_pam)
201 		do_pam_putenv(__UNCONST("KRB5CCNAME"), authctxt->krb5_ticket_file);
202 #endif
203 	debug("Kerberos v5 TGT accepted (%s)", pname);
204 
205 	restore_uid();
206 
207 	return (1);
208 
209  fail:
210 	if (problem) {
211 		errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem);
212 		if (errtxt != NULL) {
213 			debug("Kerberos v5 TGT passing failed: %s", errtxt);
214 			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
215 		} else
216 			debug("Kerberos v5 TGT passing failed: %d", problem);
217 	}
218 	if (ccache)
219 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
220 
221 	restore_uid();
222 
223 	return (0);
224 }
225 
226 
227 int
228 auth_krb5_password(Authctxt *authctxt, const char *password)
229 {
230 	krb5_error_code problem;
231 	krb5_ccache ccache = NULL;
232 	const char *errmsg;
233 
234 	temporarily_use_uid(authctxt->pw);
235 
236 	problem = krb5_init(authctxt);
237 	if (problem)
238 		goto out;
239 
240 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
241 		    &authctxt->krb5_user);
242 	if (problem)
243 		goto out;
244 
245 	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
246 	     krb5_mcc_ops.prefix, NULL, &ccache);
247 	if (problem)
248 		goto out;
249 
250 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
251 		authctxt->krb5_user);
252 	if (problem)
253 		goto out;
254 
255 	restore_uid();
256 
257 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
258 	    ccache, password, 1, NULL);
259 
260 	temporarily_use_uid(authctxt->pw);
261 
262 	if (problem)
263 		goto out;
264 
265 	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
266 	     krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache);
267 	if (problem)
268 		goto out;
269 
270 	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
271 	    authctxt->krb5_fwd_ccache);
272 	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
273 	ccache = NULL;
274 	if (problem)
275 		goto out;
276 
277 	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(
278 	    authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
279 
280  out:
281 	restore_uid();
282 
283 	if (problem) {
284 		if (ccache)
285 			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
286 
287 		if (authctxt->krb5_ctx != NULL) {
288 			errmsg = krb5_get_error_message(authctxt->krb5_ctx,
289 			    problem);
290 			debug("Kerberos password authentication failed: %s",
291 			    errmsg);
292 			krb5_free_error_message(authctxt->krb5_ctx, errmsg);
293 		} else
294 			debug("Kerberos password authentication failed: %d",
295 			    problem);
296 
297 		krb5_cleanup_proc(authctxt);
298 
299 		if (options.kerberos_or_local_passwd)
300 			return (-1);
301 		else
302 			return (0);
303 	}
304 	return (authctxt->valid ? 1 : 0);
305 }
306 
307 void
308 krb5_cleanup_proc(Authctxt *authctxt)
309 {
310 	debug("krb5_cleanup_proc called");
311 	if (authctxt->krb5_fwd_ccache) {
312 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
313 		authctxt->krb5_fwd_ccache = NULL;
314 	}
315 	if (authctxt->krb5_user) {
316 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
317 		authctxt->krb5_user = NULL;
318 	}
319 	if (authctxt->krb5_auth_ctx) {
320 		krb5_auth_con_free(authctxt->krb5_ctx,
321 		    authctxt->krb5_auth_ctx);
322 		authctxt->krb5_auth_ctx = NULL;
323 	}
324 	if (authctxt->krb5_ctx) {
325 		krb5_free_context(authctxt->krb5_ctx);
326 		authctxt->krb5_ctx = NULL;
327 	}
328 }
329 
330 #endif /* KRB5 */
331