1/* $OpenBSD: auth-krb5.c,v 1.19 2006/08/03 03:34:41 deraadt Exp $ */
2/*
3 *    Kerberos v5 authentication and ticket-passing routines.
4 *
5 * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $
6 */
7/*
8 * Copyright (c) 2002 Daniel Kouril.  All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "includes.h"
32
33#include <sys/types.h>
34#include <pwd.h>
35#include <stdarg.h>
36
37#include "xmalloc.h"
38#include "ssh.h"
39#include "ssh1.h"
40#include "packet.h"
41#include "log.h"
42#include "buffer.h"
43#include "servconf.h"
44#include "uidswap.h"
45#include "key.h"
46#include "hostfile.h"
47#include "auth.h"
48
49#ifdef KRB5
50#include <errno.h>
51#include <unistd.h>
52#include <string.h>
53#include <krb5.h>
54
55extern ServerOptions	 options;
56
57static int
58krb5_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	}
68	return (0);
69}
70
71int
72auth_krb5_password(Authctxt *authctxt, const char *password)
73{
74#ifndef HEIMDAL
75	krb5_creds creds;
76	krb5_principal server;
77#endif
78	krb5_error_code problem;
79	krb5_ccache ccache = NULL;
80	int len;
81	char *client, *platform_client;
82
83	/* get platform-specific kerberos client principal name (if it exists) */
84	platform_client = platform_krb5_get_principal_name(authctxt->pw->pw_name);
85	client = platform_client ? platform_client : authctxt->pw->pw_name;
86
87	temporarily_use_uid(authctxt->pw);
88
89	problem = krb5_init(authctxt);
90	if (problem)
91		goto out;
92
93	problem = krb5_parse_name(authctxt->krb5_ctx, client,
94		    &authctxt->krb5_user);
95	if (problem)
96		goto out;
97
98#ifdef HEIMDAL
99	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, &ccache);
100	if (problem)
101		goto out;
102
103	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
104		authctxt->krb5_user);
105	if (problem)
106		goto out;
107
108	restore_uid();
109
110	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
111	    ccache, password, 1, NULL);
112
113	temporarily_use_uid(authctxt->pw);
114
115	if (problem)
116		goto out;
117
118	problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops,
119	    &authctxt->krb5_fwd_ccache);
120	if (problem)
121		goto out;
122
123	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
124	    authctxt->krb5_fwd_ccache);
125	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
126	ccache = NULL;
127	if (problem)
128		goto out;
129
130#else
131	problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds,
132	    authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL);
133	if (problem)
134		goto out;
135
136	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
137	    KRB5_NT_SRV_HST, &server);
138	if (problem)
139		goto out;
140
141	restore_uid();
142	problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server,
143	    NULL, NULL, NULL);
144	krb5_free_principal(authctxt->krb5_ctx, server);
145	temporarily_use_uid(authctxt->pw);
146	if (problem)
147		goto out;
148
149	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, client)) {
150		problem = -1;
151		goto out;
152	}
153
154	problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, &authctxt->krb5_fwd_ccache);
155	if (problem)
156		goto out;
157
158	problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
159				     authctxt->krb5_user);
160	if (problem)
161		goto out;
162
163	problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache,
164				 &creds);
165	if (problem)
166		goto out;
167#endif
168
169	authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
170
171	len = strlen(authctxt->krb5_ticket_file) + 6;
172	authctxt->krb5_ccname = xmalloc(len);
173#ifdef USE_CCAPI
174	snprintf(authctxt->krb5_ccname, len, "API:%s",
175	    authctxt->krb5_ticket_file);
176#else
177	snprintf(authctxt->krb5_ccname, len, "FILE:%s",
178	    authctxt->krb5_ticket_file);
179#endif
180
181#ifdef USE_PAM
182	if (options.use_pam)
183		do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname);
184#endif
185
186 out:
187	restore_uid();
188
189	if (platform_client != NULL)
190		xfree(platform_client);
191
192	if (problem) {
193		if (ccache)
194			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
195
196		if (authctxt->krb5_ctx != NULL && problem!=-1)
197			debug("Kerberos password authentication failed: %s",
198			    krb5_get_err_text(authctxt->krb5_ctx, problem));
199		else
200			debug("Kerberos password authentication failed: %d",
201			    problem);
202
203		krb5_cleanup_proc(authctxt);
204
205		if (options.kerberos_or_local_passwd)
206			return (-1);
207		else
208			return (0);
209	}
210	return (authctxt->valid ? 1 : 0);
211}
212
213void
214krb5_cleanup_proc(Authctxt *authctxt)
215{
216	debug("krb5_cleanup_proc called");
217	if (authctxt->krb5_fwd_ccache) {
218		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
219		authctxt->krb5_fwd_ccache = NULL;
220	}
221	if (authctxt->krb5_user) {
222		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
223		authctxt->krb5_user = NULL;
224	}
225	if (authctxt->krb5_ctx) {
226		krb5_free_context(authctxt->krb5_ctx);
227		authctxt->krb5_ctx = NULL;
228	}
229}
230
231#ifndef HEIMDAL
232krb5_error_code
233ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
234	int tmpfd, ret, oerrno;
235	char ccname[40];
236	mode_t old_umask;
237#ifdef USE_CCAPI
238	char cctemplate[] = "API:%d:krb5cc_%d_%d";
239
240	ret = snprintf(ccname, sizeof(ccname),
241		       cctemplate, (int)geteuid(), (int)getpid(), (int)time(NULL));
242	if (ret < 0 || (size_t)ret >= sizeof(ccname))
243		return ENOMEM;
244
245#else
246	char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX";
247	int tmpfd;
248
249	ret = snprintf(ccname, sizeof(ccname),
250		       cctemplate, geteuid());
251	if (ret < 0 || (size_t)ret >= sizeof(ccname))
252		return ENOMEM;
253
254	old_umask = umask(0177);
255	tmpfd = mkstemp(ccname + strlen("FILE:"));
256	oerrno = errno;
257	umask(old_umask);
258	if (tmpfd == -1) {
259		logit("mkstemp(): %.100s", strerror(oerrno));
260		return oerrno;
261	}
262
263	if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
264		oerrno = errno;
265		logit("fchmod(): %.100s", strerror(oerrno));
266		close(tmpfd);
267		return oerrno;
268	}
269	close(tmpfd);
270#endif
271
272	return (krb5_cc_resolve(ctx, ccname, ccache));
273}
274#endif /* !HEIMDAL */
275#endif /* KRB5 */
276