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
56 extern ServerOptions options;
57
58 static int
krb5_init(void * context)59 krb5_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 */
78 int
auth_krb5(struct ssh * ssh,krb5_data * auth,char ** client,krb5_data * reply)79 auth_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
165 int
auth_krb5_tgt(Authctxt * authctxt,krb5_data * tgt)166 auth_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
230 int
auth_krb5_password(Authctxt * authctxt,const char * password)231 auth_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
310 void
krb5_cleanup_proc(Authctxt * authctxt)311 krb5_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