auth-krb5.c (b15c83408cb1e9b86c1895af0f097de05fc92ccf) auth-krb5.c (fe5fd0173b1983e53ba8dbafb3229b37444e7986)
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.
1/*
2 * 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 $
3 *
4 * $FreeBSD$
6 */
5 */
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"
6
7#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"
8#include "ssh.h"
39#include "ssh1.h"
40#include "packet.h"
9#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"
10#include "xmalloc.h"
48
49#ifdef KRB5
11
12#ifdef KRB5
50#include <errno.h>
51#include <unistd.h>
52#include <string.h>
53#include <krb5.h>
54
13
55extern ServerOptions options;
14extern krb5_context ssh_context;
15krb5_auth_context auth_context;
16krb5_ccache mem_ccache = NULL; /* Credential cache for acquired ticket */
56
17
57static int
58krb5_init(void *context)
18/* Try krb5 authentication. server_user is passed for logging purposes only,
19 in auth is received ticket, in client is returned principal from the
20 ticket */
21int
22auth_krb5(const char* server_user, krb5_data *auth, krb5_principal *client)
59{
23{
60 Authctxt *authctxt = (Authctxt *)context;
61 krb5_error_code problem;
24 krb5_error_code problem;
25 krb5_principal server = NULL;
26 krb5_principal tkt_client = NULL;
27 krb5_data reply;
28 krb5_ticket *ticket = NULL;
29 int fd;
30 int ret;
31
32 reply.length = 0;
33
34 problem = krb5_init();
35 if (problem)
36 return 0;
37
38 problem = krb5_auth_con_init(ssh_context, &auth_context);
39 if (problem) {
40 log("Kerberos v5 authentication failed: %.100s",
41 krb5_get_err_text(ssh_context, problem));
62
42
63 if (authctxt->krb5_ctx == NULL) {
64 problem = krb5_init_context(&authctxt->krb5_ctx);
65 if (problem)
66 return (problem);
43 return 0;
67 }
44 }
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,
45
46 fd = packet_get_connection_in();
47 problem = krb5_auth_con_setaddrs_from_fd(ssh_context, auth_context, &fd);
48 if (problem) {
49 ret = 0;
50 goto err;
51 }
52
53 problem = krb5_sname_to_principal(ssh_context, NULL, NULL ,
137 KRB5_NT_SRV_HST, &server);
54 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;
55 if (problem) {
56 ret = 0;
57 goto err;
152 }
58 }
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 snprintf(authctxt->krb5_ccname, len, "FILE:%s",
174 authctxt->krb5_ticket_file);
175
176#ifdef USE_PAM
177 if (options.use_pam)
178 do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname);
179#endif
180
181 out:
182 restore_uid();
183
59
184 if (platform_client != NULL)
185 xfree(platform_client);
186
60 problem = krb5_rd_req(ssh_context, &auth_context, auth, server, NULL,
61 NULL, &ticket);
187 if (problem) {
62 if (problem) {
188 if (ccache)
189 krb5_cc_destroy(authctxt->krb5_ctx, ccache);
190
191 if (authctxt->krb5_ctx != NULL && problem!=-1)
192 debug("Kerberos password authentication failed: %s",
193 krb5_get_err_text(authctxt->krb5_ctx, problem));
194 else
195 debug("Kerberos password authentication failed: %d",
196 problem);
197
198 krb5_cleanup_proc(authctxt);
199
200 if (options.kerberos_or_local_passwd)
201 return (-1);
202 else
203 return (0);
63 ret = 0;
64 goto err;
204 }
65 }
205 return (authctxt->valid ? 1 : 0);
66
67 problem = krb5_copy_principal(ssh_context, ticket->client, &tkt_client);
68 if (problem) {
69 ret = 0;
70 goto err;
71 }
72
73 /* if client wants mutual auth */
74 problem = krb5_mk_rep(ssh_context, &auth_context, &reply);
75 if (problem) {
76 ret = 0;
77 goto err;
78 }
79
80 *client = tkt_client;
81
82 packet_start(SSH_SMSG_AUTH_KRB5_RESPONSE);
83 packet_put_string((char *) reply.data, reply.length);
84 packet_send();
85 packet_write_wait();
86 ret = 1;
87
88err:
89 if (server)
90 krb5_free_principal(ssh_context, server);
91 if (ticket)
92 krb5_free_ticket(ssh_context, ticket);
93 if (reply.length)
94 xfree(reply.data);
95 return ret;
206}
207
96}
97
208void
209krb5_cleanup_proc(Authctxt *authctxt)
98int
99auth_krb5_tgt(char *server_user, krb5_data *tgt, krb5_principal tkt_client)
210{
100{
211 debug("krb5_cleanup_proc called");
212 if (authctxt->krb5_fwd_ccache) {
213 krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache);
214 authctxt->krb5_fwd_ccache = NULL;
215 }
216 if (authctxt->krb5_user) {
217 krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user);
218 authctxt->krb5_user = NULL;
219 }
220 if (authctxt->krb5_ctx) {
221 krb5_free_context(authctxt->krb5_ctx);
222 authctxt->krb5_ctx = NULL;
223 }
101 krb5_error_code problem;
102 krb5_ccache ccache = NULL;
103
104 if (ssh_context == NULL) {
105 goto fail;
106 }
107
108 problem = krb5_cc_gen_new(ssh_context, &krb5_mcc_ops, &ccache);
109 if (problem) {
110 goto fail;
111 }
112
113 problem = krb5_cc_initialize(ssh_context, ccache, tkt_client);
114 if (problem) {
115 goto fail;
116 }
117
118 problem = krb5_rd_cred(ssh_context, auth_context, ccache, tgt);
119 if (problem) {
120 goto fail;
121 }
122
123 mem_ccache = ccache;
124 ccache = NULL;
125
126 /*
127 problem = krb5_cc_copy_cache(ssh_context, ccache, mem_ccache);
128 if (problem) {
129 mem_ccache = NULL;
130 goto fail;
131 }
132
133
134 problem = krb5_cc_destroy(ssh_context, ccache);
135 if (problem)
136 goto fail;
137 */
138
139#if 0
140 packet_start(SSH_SMSG_SUCCESS);
141 packet_send();
142 packet_write_wait();
143#endif
144 return 1;
145
146fail:
147 if (ccache)
148 krb5_cc_destroy(ssh_context, ccache);
149#if 0
150 packet_start(SSH_SMSG_FAILURE);
151 packet_send();
152 packet_write_wait();
153#endif
154 return 0;
224}
225
155}
156
226#ifndef HEIMDAL
227krb5_error_code
228ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
229 int tmpfd, ret;
230 char ccname[40];
231 mode_t old_umask;
157int
158auth_krb5_password(struct passwd *pw, const char *password)
159{
160 krb5_error_code problem;
161 krb5_ccache ccache = NULL;
162 krb5_principal client = NULL;
163 int ret;
164
165 problem = krb5_init();
166 if (problem)
167 return 0;
168
169 problem = krb5_parse_name(ssh_context, pw->pw_name, &client);
170 if (problem) {
171 ret = 0;
172 goto out;
173 }
232
174
233 ret = snprintf(ccname, sizeof(ccname),
234 "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid());
235 if (ret < 0 || (size_t)ret >= sizeof(ccname))
236 return ENOMEM;
175 problem = krb5_cc_gen_new(ssh_context, &krb5_mcc_ops, &ccache);
176 if (problem) {
177 ret = 0;
178 goto out;
179 }
180
181 problem = krb5_cc_initialize(ssh_context, ccache, client);
182 if (problem) {
183 ret = 0;
184 goto out;
185 }
186
187 problem = krb5_verify_user(ssh_context, client, ccache, password, 1, NULL);
188 if (problem) {
189 ret = 0;
190 goto out;
191 }
192
193/*
194 problem = krb5_cc_copy_cache(ssh_context, ccache, mem_ccache);
195 if (problem) {
196 ret = 0;
197 mem_ccache = NULL;
198 goto out;
199 }
200 */
201 mem_ccache = ccache;
202 ccache = NULL;
203
204 ret = 1;
205out:
206 if (client != NULL)
207 krb5_free_principal(ssh_context, client);
208 if (ccache != NULL)
209 krb5_cc_destroy(ssh_context, ccache);
210 return ret;
211}
237
212
238 old_umask = umask(0177);
239 tmpfd = mkstemp(ccname + strlen("FILE:"));
240 umask(old_umask);
241 if (tmpfd == -1) {
242 logit("mkstemp(): %.100s", strerror(errno));
243 return errno;
244 }
245
246 if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
247 logit("fchmod(): %.100s", strerror(errno));
248 close(tmpfd);
249 return errno;
250 }
251 close(tmpfd);
252
253 return (krb5_cc_resolve(ctx, ccname, ccache));
213void
214krb5_cleanup_proc(void *ignore)
215{
216 extern krb5_principal tkt_client;
217
218 debug("krb5_cleanup_proc() called");
219 if (mem_ccache)
220 krb5_cc_destroy(ssh_context, mem_ccache);
221 if (tkt_client)
222 krb5_free_principal(ssh_context, tkt_client);
223 if (auth_context)
224 krb5_auth_con_free(ssh_context, auth_context);
225 if (ssh_context)
226 krb5_free_context(ssh_context);
254}
227}
255#endif /* !HEIMDAL */
228
229int
230krb5_init(void)
231{
232 krb5_error_code problem;
233 static cleanup_registered = 0;
234
235 if (ssh_context == NULL) {
236 problem = krb5_init_context(&ssh_context);
237 if (problem)
238 return problem;
239 krb5_init_ets(ssh_context);
240 }
241
242 if (!cleanup_registered) {
243 fatal_add_cleanup(krb4_cleanup_proc, NULL);
244 cleanup_registered = 1;
245 }
246 return 0;
247}
248
256#endif /* KRB5 */
249#endif /* KRB5 */