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