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"
31RCSID("$OpenBSD: auth-krb5.c,v 1.9 2002/09/09 06:48:06 itojun Exp $");
32
33#pragma ident	"%Z%%M%	%I%	%E% SMI"
34
35#include "ssh.h"
36#include "ssh1.h"
37#include "packet.h"
38#include "xmalloc.h"
39#include "log.h"
40#include "servconf.h"
41#include "uidswap.h"
42#include "auth.h"
43
44#ifdef KRB5
45#include <krb5.h>
46#ifndef HEIMDAL
47#define krb5_get_err_text(context,code) error_message(code)
48#endif /* !HEIMDAL */
49
50extern ServerOptions	 options;
51
52static int
53krb5_init(void *context)
54{
55	Authctxt *authctxt = (Authctxt *)context;
56	krb5_error_code problem;
57	static int cleanup_registered = 0;
58
59	if (authctxt->krb5_ctx == NULL) {
60		problem = krb5_init_context(&authctxt->krb5_ctx);
61		if (problem)
62			return (problem);
63		krb5_init_ets(authctxt->krb5_ctx);
64	}
65	if (!cleanup_registered) {
66		fatal_add_cleanup(krb5_cleanup_proc, authctxt);
67		cleanup_registered = 1;
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 */
77int
78auth_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
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#ifdef HEIMDAL
101	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
102	    authctxt->krb5_auth_ctx, &fd);
103#else
104	problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx,
105	    authctxt->krb5_auth_ctx,fd,
106	    KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR |
107	    KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR);
108#endif
109	if (problem)
110		goto err;
111
112	problem = krb5_sname_to_principal(authctxt->krb5_ctx,  NULL, NULL ,
113	    KRB5_NT_SRV_HST, &server);
114	if (problem)
115		goto err;
116
117	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
118	    auth, server, NULL, NULL, &ticket);
119	if (problem)
120		goto err;
121
122#ifdef HEIMDAL
123	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
124	    &authctxt->krb5_user);
125#else
126	problem = krb5_copy_principal(authctxt->krb5_ctx,
127				      ticket->enc_part2->client,
128				      &authctxt->krb5_user);
129#endif
130	if (problem)
131		goto err;
132
133	/* if client wants mutual auth */
134	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
135	    reply);
136	if (problem)
137		goto err;
138
139	/* Check .k5login authorization now. */
140	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
141	    authctxt->pw->pw_name))
142		goto err;
143
144	if (client)
145		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
146		    client);
147
148	ret = 1;
149 err:
150	if (server)
151		krb5_free_principal(authctxt->krb5_ctx, server);
152	if (ticket)
153		krb5_free_ticket(authctxt->krb5_ctx, ticket);
154	if (!ret && reply->length) {
155		xfree(reply->data);
156		memset(reply, 0, sizeof(*reply));
157	}
158
159	if (problem) {
160		if (authctxt->krb5_ctx != NULL)
161			debug("Kerberos v5 authentication failed: %s",
162			    krb5_get_err_text(authctxt->krb5_ctx, problem));
163		else
164			debug("Kerberos v5 authentication failed: %d",
165			    problem);
166	}
167
168	return (ret);
169}
170
171int
172auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
173{
174	krb5_error_code problem;
175	krb5_ccache ccache = NULL;
176	char *pname;
177	krb5_creds **creds;
178
179	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
180		return (0);
181
182	temporarily_use_uid(authctxt->pw);
183
184#ifdef HEIMDAL
185	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache);
186#else
187{
188	char ccname[40];
189	int tmpfd;
190
191	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
192
193	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
194		log("mkstemp(): %.100s", strerror(errno));
195		problem = errno;
196		goto fail;
197	}
198	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
199		log("fchmod(): %.100s", strerror(errno));
200		close(tmpfd);
201		problem = errno;
202		goto fail;
203	}
204	close(tmpfd);
205	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache);
206}
207#endif
208	if (problem)
209		goto fail;
210
211	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
212	    authctxt->krb5_user);
213	if (problem)
214		goto fail;
215
216#ifdef HEIMDAL
217	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
218	    ccache, tgt);
219	if (problem)
220		goto fail;
221#else
222	problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
223	    tgt, &creds, NULL);
224	if (problem)
225		goto fail;
226	problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds);
227	if (problem)
228		goto fail;
229#endif
230
231	authctxt->krb5_fwd_ccache = ccache;
232	ccache = NULL;
233
234	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
235
236	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
237	    &pname);
238	if (problem)
239		goto fail;
240
241	debug("Kerberos v5 TGT accepted (%s)", pname);
242
243	restore_uid();
244
245	return (1);
246
247 fail:
248	if (problem)
249		debug("Kerberos v5 TGT passing failed: %s",
250		    krb5_get_err_text(authctxt->krb5_ctx, problem));
251	if (ccache)
252		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
253
254	restore_uid();
255
256	return (0);
257}
258
259int
260auth_krb5_password(Authctxt *authctxt, const char *password)
261{
262#ifndef HEIMDAL
263	krb5_creds creds;
264	krb5_principal server;
265	char ccname[40];
266	int tmpfd;
267#endif
268	krb5_error_code problem;
269
270	if (authctxt->pw == NULL)
271		return (0);
272
273	temporarily_use_uid(authctxt->pw);
274
275	problem = krb5_init(authctxt);
276	if (problem)
277		goto out;
278
279	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
280		    &authctxt->krb5_user);
281	if (problem)
282		goto out;
283
284#ifdef HEIMDAL
285	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops,
286	    &authctxt->krb5_fwd_ccache);
287	if (problem)
288		goto out;
289
290	problem = krb5_cc_initialize(authctxt->krb5_ctx,
291	    authctxt->krb5_fwd_ccache, authctxt->krb5_user);
292	if (problem)
293		goto out;
294
295	restore_uid();
296	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
297	    authctxt->krb5_fwd_ccache, password, 1, NULL);
298	temporarily_use_uid(authctxt->pw);
299
300	if (problem)
301		goto out;
302
303#else
304	problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds,
305	    authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL);
306	if (problem)
307		goto out;
308
309	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
310	    KRB5_NT_SRV_HST, &server);
311	if (problem)
312		goto out;
313
314	restore_uid();
315	problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server,
316	    NULL, NULL, NULL);
317	krb5_free_principal(authctxt->krb5_ctx, server);
318	temporarily_use_uid(authctxt->pw);
319	if (problem)
320		goto out;
321
322	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
323			  authctxt->pw->pw_name)) {
324		problem = -1;
325		goto out;
326	}
327
328	snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid());
329
330	if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) {
331		log("mkstemp(): %.100s", strerror(errno));
332		problem = errno;
333		goto out;
334	}
335
336	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
337		log("fchmod(): %.100s", strerror(errno));
338		close(tmpfd);
339		problem = errno;
340		goto out;
341	}
342	close(tmpfd);
343
344	problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache);
345	if (problem)
346		goto out;
347
348	problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
349				     authctxt->krb5_user);
350	if (problem)
351		goto out;
352
353	problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
354				 &creds);
355	if (problem)
356		goto out;
357#endif
358
359	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
360
361 out:
362	restore_uid();
363
364	if (problem) {
365		if (authctxt->krb5_ctx != NULL && problem!=-1)
366			debug("Kerberos password authentication failed: %s",
367			    krb5_get_err_text(authctxt->krb5_ctx, problem));
368		else
369			debug("Kerberos password authentication failed: %d",
370			    problem);
371
372		krb5_cleanup_proc(authctxt);
373
374		if (options.kerberos_or_local_passwd)
375			return (-1);
376		else
377			return (0);
378	}
379	return (1);
380}
381
382void
383krb5_cleanup_proc(void *context)
384{
385	Authctxt *authctxt = (Authctxt *)context;
386
387	debug("krb5_cleanup_proc called");
388	if (authctxt->krb5_fwd_ccache) {
389		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
390		authctxt->krb5_fwd_ccache = NULL;
391	}
392	if (authctxt->krb5_user) {
393		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
394		authctxt->krb5_user = NULL;
395	}
396	if (authctxt->krb5_auth_ctx) {
397		krb5_auth_con_free(authctxt->krb5_ctx,
398		    authctxt->krb5_auth_ctx);
399		authctxt->krb5_auth_ctx = NULL;
400	}
401	if (authctxt->krb5_ctx) {
402		krb5_free_context(authctxt->krb5_ctx);
403		authctxt->krb5_ctx = NULL;
404	}
405}
406
407#endif /* KRB5 */
408