xref: /netbsd-src/crypto/external/bsd/openssh/dist/auth-krb5.c (revision e8c0841bec275001117d7559c0b7638bc3633384)
1 /*	$NetBSD: auth-krb5.c,v 1.16 2021/04/19 14:40:15 christos Exp $	*/
2 /* $OpenBSD: auth-krb5.c,v 1.24 2021/04/03 06:18:40 djm 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.16 2021/04/19 14:40:15 christos Exp $");
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include <pwd.h>
38 #include <stdarg.h>
39 #include <string.h>
40 
41 #include "xmalloc.h"
42 #include "ssh.h"
43 #include "misc.h"
44 #include "packet.h"
45 #include "log.h"
46 #include "sshbuf.h"
47 #include "sshkey.h"
48 #include "servconf.h"
49 #include "uidswap.h"
50 #include "hostfile.h"
51 #include "auth.h"
52 
53 #ifdef KRB5
54 #include <krb5.h>
55 
56 extern ServerOptions	 options;
57 
58 static int
krb5_init(void * context)59 krb5_init(void *context)
60 {
61 	Authctxt *authctxt = (Authctxt *)context;
62 	krb5_error_code problem;
63 
64 	if (authctxt->krb5_ctx == NULL) {
65 		problem = krb5_init_context(&authctxt->krb5_ctx);
66 		if (problem)
67 			return (problem);
68 		krb5_init_ets(authctxt->krb5_ctx);
69 	}
70 	return (0);
71 }
72 
73 /*
74  * Try krb5 authentication. server_user is passed for logging purposes
75  * only, in auth is received ticket, in client is returned principal
76  * from the ticket
77  */
78 int
auth_krb5(struct ssh * ssh,krb5_data * auth,char ** client,krb5_data * reply)79 auth_krb5(struct ssh *ssh, krb5_data *auth, char **client, krb5_data *reply)
80 {
81 	krb5_error_code problem;
82 	krb5_principal server;
83 	krb5_ticket *ticket;
84 	int fd, ret;
85 	const char *errtxt;
86 	Authctxt *authctxt = ssh->authctxt;
87 
88 	ret = 0;
89 	server = NULL;
90 	ticket = NULL;
91 	reply->length = 0;
92 
93 	problem = krb5_init(authctxt);
94 	if (problem)
95 		goto err;
96 
97 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
98 	    &authctxt->krb5_auth_ctx);
99 	if (problem)
100 		goto err;
101 
102 	fd = ssh_packet_get_connection_in(ssh);
103 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
104 	    authctxt->krb5_auth_ctx, &fd);
105 	if (problem)
106 		goto err;
107 
108 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
109 	    KRB5_NT_SRV_HST, &server);
110 	if (problem)
111 		goto err;
112 
113 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
114 	    auth, server, NULL, NULL, &ticket);
115 	if (problem)
116 		goto err;
117 
118 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
119 	    &authctxt->krb5_user);
120 	if (problem)
121 		goto err;
122 
123 	/* if client wants mutual auth */
124 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
125 	    reply);
126 	if (problem)
127 		goto err;
128 
129 	/* Check .k5login authorization now. */
130 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
131 	    authctxt->pw->pw_name))
132 		goto err;
133 
134 	if (client)
135 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
136 		    client);
137 
138 	ret = 1;
139  err:
140 	if (server)
141 		krb5_free_principal(authctxt->krb5_ctx, server);
142 	if (ticket)
143 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
144 	if (!ret && reply->length) {
145 		free(reply->data);
146 		memset(reply, 0, sizeof(*reply));
147 	}
148 
149 	if (problem) {
150 		errtxt = NULL;
151 		if (authctxt->krb5_ctx != NULL)
152 			errtxt = krb5_get_error_message(authctxt->krb5_ctx,
153 			    problem);
154 		if (errtxt != NULL) {
155 			debug("Kerberos v5 authentication failed: %s", errtxt);
156 			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
157 		} else
158 			debug("Kerberos v5 authentication failed: %d",
159 			    problem);
160 	}
161 
162 	return (ret);
163 }
164 
165 int
auth_krb5_tgt(Authctxt * authctxt,krb5_data * tgt)166 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
167 {
168 	krb5_error_code problem;
169 	krb5_ccache ccache = NULL;
170 	char *pname;
171 	const char *errtxt;
172 
173 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
174 		return (0);
175 
176 	temporarily_use_uid(authctxt->pw);
177 
178 	problem = krb5_cc_new_unique(authctxt->krb5_ctx, "FILE", NULL, &ccache);
179 	if (problem)
180 		goto fail;
181 
182 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
183 	    authctxt->krb5_user);
184 	if (problem)
185 		goto fail;
186 
187 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
188 	    ccache, tgt);
189 	if (problem)
190 		goto fail;
191 
192 	authctxt->krb5_fwd_ccache = ccache;
193 	ccache = NULL;
194 
195 	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
196 
197 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
198 	    &pname);
199 	if (problem)
200 		goto fail;
201 
202 #ifdef USE_PAM
203 	if (options.use_pam)
204 		do_pam_putenv(__UNCONST("KRB5CCNAME"), authctxt->krb5_ticket_file);
205 #endif
206 	debug("Kerberos v5 TGT accepted (%s)", pname);
207 
208 	restore_uid();
209 
210 	return (1);
211 
212  fail:
213 	if (problem) {
214 		errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem);
215 		if (errtxt != NULL) {
216 			debug("Kerberos v5 TGT passing failed: %s", errtxt);
217 			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
218 		} else
219 			debug("Kerberos v5 TGT passing failed: %d", problem);
220 	}
221 	if (ccache)
222 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
223 
224 	restore_uid();
225 
226 	return (0);
227 }
228 
229 
230 int
auth_krb5_password(Authctxt * authctxt,const char * password)231 auth_krb5_password(Authctxt *authctxt, const char *password)
232 {
233 	krb5_error_code problem;
234 	krb5_ccache ccache = NULL;
235 	const char *errmsg;
236 
237 	temporarily_use_uid(authctxt->pw);
238 
239 	problem = krb5_init(authctxt);
240 	if (problem)
241 		goto out;
242 
243 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
244 		    &authctxt->krb5_user);
245 	if (problem)
246 		goto out;
247 
248 	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
249 	    krb5_mcc_ops.prefix, NULL, &ccache);
250 	if (problem)
251 		goto out;
252 
253 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
254 		authctxt->krb5_user);
255 	if (problem)
256 		goto out;
257 
258 	restore_uid();
259 
260 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
261 	    ccache, password, 1, NULL);
262 
263 	temporarily_use_uid(authctxt->pw);
264 
265 	if (problem)
266 		goto out;
267 
268 	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
269 	    krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache);
270 	if (problem)
271 		goto out;
272 
273 	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
274 	    authctxt->krb5_fwd_ccache);
275 	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
276 	ccache = NULL;
277 	if (problem)
278 		goto out;
279 
280 	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(
281 	    authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
282 
283  out:
284 	restore_uid();
285 
286 	if (problem) {
287 		if (ccache)
288 			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
289 
290 		if (authctxt->krb5_ctx != NULL) {
291 			errmsg = krb5_get_error_message(authctxt->krb5_ctx,
292 			    problem);
293 			debug("Kerberos password authentication failed: %s",
294 			    errmsg);
295 			krb5_free_error_message(authctxt->krb5_ctx, errmsg);
296 		} else
297 			debug("Kerberos password authentication failed: %d",
298 			    problem);
299 
300 		krb5_cleanup_proc(authctxt);
301 
302 		if (options.kerberos_or_local_passwd)
303 			return (-1);
304 		else
305 			return (0);
306 	}
307 	return (authctxt->valid ? 1 : 0);
308 }
309 
310 void
krb5_cleanup_proc(Authctxt * authctxt)311 krb5_cleanup_proc(Authctxt *authctxt)
312 {
313 	debug("krb5_cleanup_proc called");
314 	if (authctxt->krb5_fwd_ccache) {
315 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
316 		authctxt->krb5_fwd_ccache = NULL;
317 	}
318 	if (authctxt->krb5_user) {
319 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
320 		authctxt->krb5_user = NULL;
321 	}
322 	if (authctxt->krb5_auth_ctx) {
323 		krb5_auth_con_free(authctxt->krb5_ctx,
324 		    authctxt->krb5_auth_ctx);
325 		authctxt->krb5_auth_ctx = NULL;
326 	}
327 	if (authctxt->krb5_ctx) {
328 		krb5_free_context(authctxt->krb5_ctx);
329 		authctxt->krb5_ctx = NULL;
330 	}
331 }
332 
333 #endif /* KRB5 */
334