1 /*	$NetBSD: auth-krb5.c,v 1.2 2009/06/07 22:38:46 christos Exp $	*/
2 /* $OpenBSD: auth-krb5.c,v 1.19 2006/08/03 03:34:41 deraadt 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.2 2009/06/07 22:38:46 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 
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 	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
100 	    authctxt->krb5_auth_ctx, &fd);
101 	if (problem)
102 		goto err;
103 
104 	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
105 	    KRB5_NT_SRV_HST, &server);
106 	if (problem)
107 		goto err;
108 
109 	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
110 	    auth, server, NULL, NULL, &ticket);
111 	if (problem)
112 		goto err;
113 
114 	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
115 	    &authctxt->krb5_user);
116 	if (problem)
117 		goto err;
118 
119 	/* if client wants mutual auth */
120 	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
121 	    reply);
122 	if (problem)
123 		goto err;
124 
125 	/* Check .k5login authorization now. */
126 	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
127 	    authctxt->pw->pw_name))
128 		goto err;
129 
130 	if (client)
131 		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
132 		    client);
133 
134 	ret = 1;
135  err:
136 	if (server)
137 		krb5_free_principal(authctxt->krb5_ctx, server);
138 	if (ticket)
139 		krb5_free_ticket(authctxt->krb5_ctx, ticket);
140 	if (!ret && reply->length) {
141 		xfree(reply->data);
142 		memset(reply, 0, sizeof(*reply));
143 	}
144 
145 	if (problem) {
146 		if (authctxt->krb5_ctx != NULL)
147 			debug("Kerberos v5 authentication failed: %s",
148 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
149 		else
150 			debug("Kerberos v5 authentication failed: %d",
151 			    problem);
152 	}
153 
154 	return (ret);
155 }
156 
157 int
158 auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
159 {
160 	krb5_error_code problem;
161 	krb5_ccache ccache = NULL;
162 	char *pname;
163 
164 	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
165 		return (0);
166 
167 	temporarily_use_uid(authctxt->pw);
168 
169 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
170 	if (problem)
171 		goto fail;
172 
173 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
174 	    authctxt->krb5_user);
175 	if (problem)
176 		goto fail;
177 
178 	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
179 	    ccache, tgt);
180 	if (problem)
181 		goto fail;
182 
183 	authctxt->krb5_fwd_ccache = ccache;
184 	ccache = NULL;
185 
186 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
187 
188 	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
189 	    &pname);
190 	if (problem)
191 		goto fail;
192 
193 #ifdef USE_PAM
194 	if (options.use_pam)
195 		do_pam_putenv("KRB5CCNAME", authctxt->krb5_ticket_file);
196 #endif
197 	debug("Kerberos v5 TGT accepted (%s)", pname);
198 
199 	restore_uid();
200 
201 	return (1);
202 
203  fail:
204 	if (problem)
205 		debug("Kerberos v5 TGT passing failed: %s",
206 		    krb5_get_err_text(authctxt->krb5_ctx, problem));
207 	if (ccache)
208 		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
209 
210 	restore_uid();
211 
212 	return (0);
213 }
214 
215 
216 int
217 auth_krb5_password(Authctxt *authctxt, const char *password)
218 {
219 	krb5_error_code problem;
220 	krb5_ccache ccache = NULL;
221 
222 	temporarily_use_uid(authctxt->pw);
223 
224 	problem = krb5_init(authctxt);
225 	if (problem)
226 		goto out;
227 
228 	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
229 		    &authctxt->krb5_user);
230 	if (problem)
231 		goto out;
232 
233 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, &ccache);
234 	if (problem)
235 		goto out;
236 
237 	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
238 		authctxt->krb5_user);
239 	if (problem)
240 		goto out;
241 
242 	restore_uid();
243 
244 	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
245 	    ccache, password, 1, NULL);
246 
247 	temporarily_use_uid(authctxt->pw);
248 
249 	if (problem)
250 		goto out;
251 
252 	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops,
253 	    &authctxt->krb5_fwd_ccache);
254 	if (problem)
255 		goto out;
256 
257 	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
258 	    authctxt->krb5_fwd_ccache);
259 	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
260 	ccache = NULL;
261 	if (problem)
262 		goto out;
263 
264 	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx,
265 	    authctxt->krb5_fwd_ccache);
266 
267  out:
268 	restore_uid();
269 
270 	if (problem) {
271 		if (ccache)
272 			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
273 
274 		if (authctxt->krb5_ctx != NULL)
275 			debug("Kerberos password authentication failed: %s",
276 			    krb5_get_err_text(authctxt->krb5_ctx, problem));
277 		else
278 			debug("Kerberos password authentication failed: %d",
279 			    problem);
280 
281 		krb5_cleanup_proc(authctxt);
282 
283 		if (options.kerberos_or_local_passwd)
284 			return (-1);
285 		else
286 			return (0);
287 	}
288 	return (authctxt->valid ? 1 : 0);
289 }
290 
291 void
292 krb5_cleanup_proc(Authctxt *authctxt)
293 {
294 	debug("krb5_cleanup_proc called");
295 	if (authctxt->krb5_fwd_ccache) {
296 		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
297 		authctxt->krb5_fwd_ccache = NULL;
298 	}
299 	if (authctxt->krb5_user) {
300 		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
301 		authctxt->krb5_user = NULL;
302 	}
303 	if (authctxt->krb5_auth_ctx) {
304 		krb5_auth_con_free(authctxt->krb5_ctx,
305 		    authctxt->krb5_auth_ctx);
306 		authctxt->krb5_auth_ctx = NULL;
307 	}
308 	if (authctxt->krb5_ctx) {
309 		krb5_free_context(authctxt->krb5_ctx);
310 		authctxt->krb5_ctx = NULL;
311 	}
312 }
313 
314 #endif /* KRB5 */
315