1/*	$NetBSD: auth-krb5.c,v 1.16 2021/04/19 14:40:15 christos Exp $	*/
2/* $OpenBSD: auth-krb5.c,v 1.24 2021/04/03 06:18:40 djm Exp $ */
3
4/*
5 *    Kerberos v5 authentication and ticket-passing routines.
6 *
7 * From: FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar
8 */
9/*
10 * Copyright (c) 2002 Daniel Kouril.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "includes.h"
34__RCSID("$NetBSD: auth-krb5.c,v 1.16 2021/04/19 14:40:15 christos Exp $");
35#include <sys/types.h>
36#include <stdio.h>
37#include <pwd.h>
38#include <stdarg.h>
39#include <string.h>
40
41#include "xmalloc.h"
42#include "ssh.h"
43#include "misc.h"
44#include "packet.h"
45#include "log.h"
46#include "sshbuf.h"
47#include "sshkey.h"
48#include "servconf.h"
49#include "uidswap.h"
50#include "hostfile.h"
51#include "auth.h"
52
53#ifdef KRB5
54#include <krb5.h>
55
56extern ServerOptions	 options;
57
58static int
59krb5_init(void *context)
60{
61	Authctxt *authctxt = (Authctxt *)context;
62	krb5_error_code problem;
63
64	if (authctxt->krb5_ctx == NULL) {
65		problem = krb5_init_context(&authctxt->krb5_ctx);
66		if (problem)
67			return (problem);
68		krb5_init_ets(authctxt->krb5_ctx);
69	}
70	return (0);
71}
72
73/*
74 * Try krb5 authentication. server_user is passed for logging purposes
75 * only, in auth is received ticket, in client is returned principal
76 * from the ticket
77 */
78int
79auth_krb5(struct ssh *ssh, krb5_data *auth, char **client, krb5_data *reply)
80{
81	krb5_error_code problem;
82	krb5_principal server;
83	krb5_ticket *ticket;
84	int fd, ret;
85	const char *errtxt;
86	Authctxt *authctxt = ssh->authctxt;
87
88	ret = 0;
89	server = NULL;
90	ticket = NULL;
91	reply->length = 0;
92
93	problem = krb5_init(authctxt);
94	if (problem)
95		goto err;
96
97	problem = krb5_auth_con_init(authctxt->krb5_ctx,
98	    &authctxt->krb5_auth_ctx);
99	if (problem)
100		goto err;
101
102	fd = ssh_packet_get_connection_in(ssh);
103	problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx,
104	    authctxt->krb5_auth_ctx, &fd);
105	if (problem)
106		goto err;
107
108	problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL,
109	    KRB5_NT_SRV_HST, &server);
110	if (problem)
111		goto err;
112
113	problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx,
114	    auth, server, NULL, NULL, &ticket);
115	if (problem)
116		goto err;
117
118	problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client,
119	    &authctxt->krb5_user);
120	if (problem)
121		goto err;
122
123	/* if client wants mutual auth */
124	problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
125	    reply);
126	if (problem)
127		goto err;
128
129	/* Check .k5login authorization now. */
130	if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user,
131	    authctxt->pw->pw_name))
132		goto err;
133
134	if (client)
135		krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
136		    client);
137
138	ret = 1;
139 err:
140	if (server)
141		krb5_free_principal(authctxt->krb5_ctx, server);
142	if (ticket)
143		krb5_free_ticket(authctxt->krb5_ctx, ticket);
144	if (!ret && reply->length) {
145		free(reply->data);
146		memset(reply, 0, sizeof(*reply));
147	}
148
149	if (problem) {
150		errtxt = NULL;
151		if (authctxt->krb5_ctx != NULL)
152			errtxt = krb5_get_error_message(authctxt->krb5_ctx,
153			    problem);
154		if (errtxt != NULL) {
155			debug("Kerberos v5 authentication failed: %s", errtxt);
156			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
157		} else
158			debug("Kerberos v5 authentication failed: %d",
159			    problem);
160	}
161
162	return (ret);
163}
164
165int
166auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt)
167{
168	krb5_error_code problem;
169	krb5_ccache ccache = NULL;
170	char *pname;
171	const char *errtxt;
172
173	if (authctxt->pw == NULL || authctxt->krb5_user == NULL)
174		return (0);
175
176	temporarily_use_uid(authctxt->pw);
177
178	problem = krb5_cc_new_unique(authctxt->krb5_ctx, "FILE", NULL, &ccache);
179	if (problem)
180		goto fail;
181
182	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
183	    authctxt->krb5_user);
184	if (problem)
185		goto fail;
186
187	problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,
188	    ccache, tgt);
189	if (problem)
190		goto fail;
191
192	authctxt->krb5_fwd_ccache = ccache;
193	ccache = NULL;
194
195	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
196
197	problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user,
198	    &pname);
199	if (problem)
200		goto fail;
201
202#ifdef USE_PAM
203	if (options.use_pam)
204		do_pam_putenv(__UNCONST("KRB5CCNAME"), authctxt->krb5_ticket_file);
205#endif
206	debug("Kerberos v5 TGT accepted (%s)", pname);
207
208	restore_uid();
209
210	return (1);
211
212 fail:
213	if (problem) {
214		errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem);
215		if (errtxt != NULL) {
216			debug("Kerberos v5 TGT passing failed: %s", errtxt);
217			krb5_free_error_message(authctxt->krb5_ctx, errtxt);
218		} else
219			debug("Kerberos v5 TGT passing failed: %d", problem);
220	}
221	if (ccache)
222		krb5_cc_destroy(authctxt->krb5_ctx, ccache);
223
224	restore_uid();
225
226	return (0);
227}
228
229
230int
231auth_krb5_password(Authctxt *authctxt, const char *password)
232{
233	krb5_error_code problem;
234	krb5_ccache ccache = NULL;
235	const char *errmsg;
236
237	temporarily_use_uid(authctxt->pw);
238
239	problem = krb5_init(authctxt);
240	if (problem)
241		goto out;
242
243	problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name,
244		    &authctxt->krb5_user);
245	if (problem)
246		goto out;
247
248	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
249	    krb5_mcc_ops.prefix, NULL, &ccache);
250	if (problem)
251		goto out;
252
253	problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache,
254		authctxt->krb5_user);
255	if (problem)
256		goto out;
257
258	restore_uid();
259
260	problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user,
261	    ccache, password, 1, NULL);
262
263	temporarily_use_uid(authctxt->pw);
264
265	if (problem)
266		goto out;
267
268	problem = krb5_cc_new_unique(authctxt->krb5_ctx,
269	    krb5_fcc_ops.prefix, NULL, &authctxt->krb5_fwd_ccache);
270	if (problem)
271		goto out;
272
273	problem = krb5_cc_copy_cache(authctxt->krb5_ctx, ccache,
274	    authctxt->krb5_fwd_ccache);
275	krb5_cc_destroy(authctxt->krb5_ctx, ccache);
276	ccache = NULL;
277	if (problem)
278		goto out;
279
280	authctxt->krb5_ticket_file = __UNCONST(krb5_cc_get_name(
281	    authctxt->krb5_ctx, authctxt->krb5_fwd_ccache));
282
283 out:
284	restore_uid();
285
286	if (problem) {
287		if (ccache)
288			krb5_cc_destroy(authctxt->krb5_ctx, ccache);
289
290		if (authctxt->krb5_ctx != NULL) {
291			errmsg = krb5_get_error_message(authctxt->krb5_ctx,
292			    problem);
293			debug("Kerberos password authentication failed: %s",
294			    errmsg);
295			krb5_free_error_message(authctxt->krb5_ctx, errmsg);
296		} else
297			debug("Kerberos password authentication failed: %d",
298			    problem);
299
300		krb5_cleanup_proc(authctxt);
301
302		if (options.kerberos_or_local_passwd)
303			return (-1);
304		else
305			return (0);
306	}
307	return (authctxt->valid ? 1 : 0);
308}
309
310void
311krb5_cleanup_proc(Authctxt *authctxt)
312{
313	debug("krb5_cleanup_proc called");
314	if (authctxt->krb5_fwd_ccache) {
315		krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
316		authctxt->krb5_fwd_ccache = NULL;
317	}
318	if (authctxt->krb5_user) {
319		krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
320		authctxt->krb5_user = NULL;
321	}
322	if (authctxt->krb5_auth_ctx) {
323		krb5_auth_con_free(authctxt->krb5_ctx,
324		    authctxt->krb5_auth_ctx);
325		authctxt->krb5_auth_ctx = NULL;
326	}
327	if (authctxt->krb5_ctx) {
328		krb5_free_context(authctxt->krb5_ctx);
329		authctxt->krb5_ctx = NULL;
330	}
331}
332
333#endif /* KRB5 */
334