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