xref: /openbsd-src/usr.bin/ssh/auth-krb5.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*
2  *    Kerberos v5 authentication and ticket-passing routines.
3  *
4  * $FreeBSD: 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.11 2003/07/16 15:02:06 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 
45 extern ServerOptions	 options;
46 
47 static int
48 krb5_init(void *context)
49 {
50 	Authctxt *authctxt = (Authctxt *)context;
51 	krb5_error_code problem;
52 	static int cleanup_registered = 0;
53 
54 	if (authctxt->krb5_ctx == NULL) {
55 		problem = krb5_init_context(&authctxt->krb5_ctx);
56 		if (problem)
57 			return (problem);
58 		krb5_init_ets(authctxt->krb5_ctx);
59 	}
60 	if (!cleanup_registered) {
61 		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
62 		cleanup_registered = 1;
63 	}
64 	return (0);
65 }
66 
67 /*
68  * Try krb5 authentication. server_user is passed for logging purposes
69  * only, in auth is received ticket, in client is returned principal
70  * from the ticket
71  */
72 int
73 auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply)
74 {
75 	krb5_error_code problem;
76 	krb5_principal server;
77 	krb5_ticket *ticket;
78 	int fd, ret;
79 
80 	ret = 0;
81 	server = NULL;
82 	ticket = NULL;
83 	reply->length = 0;
84 
85 	problem = krb5_init(authctxt);
86 	if (problem)
87 		goto err;
88 
89 	problem = krb5_auth_con_init(authctxt->krb5_ctx,
90 	    &authctxt->krb5_auth_ctx);
91 	if (problem)
92 		goto err;
93 
94 	fd = packet_get_connection_in();
95 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
96 	    authctxt->krb5_auth_ctx, &fd);
97 	if (problem)
98 		goto err;
99 
100 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
101 	    KRB5_NT_SRV_HST, &server);
102 	if (problem)
103 		goto err;
104 
105 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
106 	    auth, server, NULL, NULL, &ticket);
107 	if (problem)
108 		goto err;
109 
110 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
111 	    &authctxt->krb5_user);
112 	if (problem)
113 		goto err;
114 
115 	/* if client wants mutual auth */
116 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
117 	    reply);
118 	if (problem)
119 		goto err;
120 
121 	/* Check .k5login authorization now. */
122 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
123 	    authctxt->pw->pw_name))
124 		goto err;
125 
126 	if (client)
127 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
128 		    client);
129 
130 	ret = 1;
131  err:
132 	if (server)
133 		krb5_free_principal(authctxt->krb5_ctx, server);
134 	if (ticket)
135 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
136 	if (!ret && reply->length) {
137 		xfree(reply->data);
138 		memset(reply, 0, sizeof(*reply));
139 	}
140 
141 	if (problem) {
142 		if (authctxt->krb5_ctx != NULL)
143 			debug("Kerberos v5 authentication failed: %s",
144 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
145 		else
146 			debug("Kerberos v5 authentication failed: %d",
147 			    problem);
148 	}
149 
150 	return (ret);
151 }
152 
153 int
154 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
155 {
156 	krb5_error_code problem;
157 	krb5_ccache ccache = NULL;
158 	char *pname;
159 
160 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
161 		return (0);
162 
163 	temporarily_use_uid(authctxt->pw);
164 
165 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
166 	if (problem)
167 		goto fail;
168 
169 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
170 	    authctxt->krb5_user);
171 	if (problem)
172 		goto fail;
173 
174 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
175 	    ccache, tgt);
176 	if (problem)
177 		goto fail;
178 
179 	authctxt->krb5_fwd_ccache = ccache;
180 	ccache = NULL;
181 
182 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
183 
184 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
185 	    &pname);
186 	if (problem)
187 		goto fail;
188 
189 	debug("Kerberos v5 TGT accepted (%s)", pname);
190 
191 	restore_uid();
192 
193 	return (1);
194 
195  fail:
196 	if (problem)
197 		debug("Kerberos v5 TGT passing failed: %s",
198 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
199 	if (ccache)
200 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
201 
202 	restore_uid();
203 
204 	return (0);
205 }
206 
207 int
208 auth_krb5_password(Authctxt *authctxt, const char *password)
209 {
210 	krb5_error_code problem;
211 	krb5_ccache ccache = NULL;
212 
213 	if (authctxt->pw == NULL)
214 		return (0);
215 
216 	temporarily_use_uid(authctxt->pw);
217 
218 	problem = krb5_init(authctxt);
219 	if (problem)
220 		goto out;
221 
222 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
223 		    &authctxt->krb5_user);
224 	if (problem)
225 		goto out;
226 
227 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, &ccache);
228 	if (problem)
229 		goto out;
230 
231 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
232 		authctxt->krb5_user);
233 	if (problem)
234 		goto out;
235 
236 	restore_uid();
237 
238 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
239 	    ccache, password, 1, NULL);
240 
241 	temporarily_use_uid(authctxt->pw);
242 
243 	if (problem)
244 		goto out;
245 
246 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops,
247 	    &authctxt->krb5_fwd_ccache);
248 	if (problem)
249 		goto out;
250 
251 	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
252 	    authctxt->krb5_fwd_ccache);
253 	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
254 	ccache = NULL;
255 	if (problem)
256 		goto out;
257 
258 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
259 
260  out:
261 	restore_uid();
262 
263 	if (problem) {
264 		if (ccache)
265 			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
266 
267 		if (authctxt->krb5_ctx != NULL)
268 			debug("Kerberos password authentication failed: %s",
269 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
270 		else
271 			debug("Kerberos password authentication failed: %d",
272 			    problem);
273 
274 		krb5_cleanup_proc(authctxt);
275 
276 		if (options.kerberos_or_local_passwd)
277 			return (-1);
278 		else
279 			return (0);
280 	}
281 	return (1);
282 }
283 
284 void
285 krb5_cleanup_proc(void *context)
286 {
287 	Authctxt *authctxt = (Authctxt *)context;
288 
289 	debug("krb5_cleanup_proc called");
290 	if (authctxt->krb5_fwd_ccache) {
291 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
292 		authctxt->krb5_fwd_ccache = NULL;
293 	}
294 	if (authctxt->krb5_user) {
295 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
296 		authctxt->krb5_user = NULL;
297 	}
298 	if (authctxt->krb5_auth_ctx) {
299 		krb5_auth_con_free(authctxt->krb5_ctx,
300 		    authctxt->krb5_auth_ctx);
301 		authctxt->krb5_auth_ctx = NULL;
302 	}
303 	if (authctxt->krb5_ctx) {
304 		krb5_free_context(authctxt->krb5_ctx);
305 		authctxt->krb5_ctx = NULL;
306 	}
307 }
308 
309 #endif /* KRB5 */
310