1 | /* |
---|
2 | |
---|
3 | auth-kerberos.c |
---|
4 | |
---|
5 | Initally written by Dug Song <dugsong@umich.edu> for Kerberos V4. |
---|
6 | Mapped over to user Kerberos V5 by Glenn Machin - Sandia Natl Labs |
---|
7 | |
---|
8 | Kerberos authentication and ticket-passing routines. |
---|
9 | |
---|
10 | */ |
---|
11 | /* |
---|
12 | * $Id: auth-kerberos.c,v 1.11 1999-03-08 18:20:00 danw Exp $ |
---|
13 | * $Log: not supported by cvs2svn $ |
---|
14 | * Revision 1.10 1998/11/09 16:25:21 ghudson |
---|
15 | * Close some possible buffer overflows. |
---|
16 | * |
---|
17 | * Revision 1.9 1998/08/01 18:38:49 danw |
---|
18 | * from amu: malloc enough space to store the full ticket file name |
---|
19 | * |
---|
20 | * Revision 1.8 1998/07/28 03:15:50 ghudson |
---|
21 | * Dan didn't actually just leave out a right paren; he put in a |
---|
22 | * semicolon instead. Nuke the semicolon. |
---|
23 | * |
---|
24 | * Revision 1.7 1998/07/15 22:48:53 ghudson |
---|
25 | * Dan left out a right paren. |
---|
26 | * |
---|
27 | * Revision 1.6 1998/07/14 17:34:49 danw |
---|
28 | * use krb5_build_principal_ext instead of krb5_build_principal in case |
---|
29 | * the realm data isn't NUL-terminated |
---|
30 | * |
---|
31 | * Revision 1.5 1998/01/24 01:47:20 danw |
---|
32 | * merge in changes for 1.2.22 |
---|
33 | * |
---|
34 | * Revision 1.4 1997/11/19 20:44:42 danw |
---|
35 | * do chown later |
---|
36 | * |
---|
37 | * Revision 1.3 1997/11/15 00:04:12 danw |
---|
38 | * Use atexit() functions to destroy tickets and call al_acct_revert. |
---|
39 | * Work around Solaris lossage with libucb and grantpt. |
---|
40 | * |
---|
41 | * Revision 1.2 1997/11/12 21:16:08 danw |
---|
42 | * Athena-login changes (including some krb4 stuff) |
---|
43 | * |
---|
44 | * Revision 1.1.1.1 1997/10/17 22:26:14 danw |
---|
45 | * Import of ssh 1.2.21 |
---|
46 | * |
---|
47 | * Revision 1.1.1.2 1998/01/24 01:25:35 danw |
---|
48 | * Import of ssh 1.2.22 |
---|
49 | * |
---|
50 | * Revision 1.1.1.3 1999/03/08 17:43:32 danw |
---|
51 | * Import of ssh 1.2.26 |
---|
52 | * |
---|
53 | * Revision 1.3 1998/01/02 06:13:56 kivinen |
---|
54 | * Fixed kerberos ticket allocation. |
---|
55 | * |
---|
56 | * Revision 1.2 1997/04/17 03:56:51 kivinen |
---|
57 | * Kept FILE: prefix in kerberos ticket filename as DCE cache |
---|
58 | * code requires it (patch from Doug Engert <DEEngert@anl.gov>). |
---|
59 | * |
---|
60 | * Revision 1.1 1997/03/27 03:09:29 kivinen |
---|
61 | * *** empty log message *** |
---|
62 | * |
---|
63 | * |
---|
64 | * $Endlog$ |
---|
65 | */ |
---|
66 | |
---|
67 | #include "includes.h" |
---|
68 | #include "packet.h" |
---|
69 | #include "xmalloc.h" |
---|
70 | #include "ssh.h" |
---|
71 | |
---|
72 | #ifdef KERBEROS |
---|
73 | #if defined (KRB5) |
---|
74 | #include <krb5.h> |
---|
75 | /* kludge to allow us to #include krb.h without namespace conflicts */ |
---|
76 | #define des_cbc_encrypt krb_des_cbc_encrypt |
---|
77 | #include <krb.h> |
---|
78 | |
---|
79 | extern krb5_context ssh_context; |
---|
80 | extern krb5_auth_context auth_context; |
---|
81 | extern int havecred; |
---|
82 | void krb_cleanup(void); |
---|
83 | |
---|
84 | int auth_kerberos(char *server_user, krb5_data *auth, krb5_principal *client) |
---|
85 | { |
---|
86 | krb5_error_code problem; |
---|
87 | krb5_ticket *ticket; |
---|
88 | krb5_data reply; |
---|
89 | krb5_principal tkt_client; |
---|
90 | char *server = 0; |
---|
91 | |
---|
92 | memset(&reply, 0, sizeof(reply)); |
---|
93 | |
---|
94 | if (auth_context) |
---|
95 | krb5_auth_con_free(ssh_context, auth_context); |
---|
96 | |
---|
97 | auth_context = 0; |
---|
98 | krb5_auth_con_init(ssh_context, &auth_context); |
---|
99 | problem = |
---|
100 | krb5_auth_con_genaddrs(ssh_context, auth_context, |
---|
101 | packet_get_connection_in(), |
---|
102 | KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR); |
---|
103 | if (problem) |
---|
104 | { |
---|
105 | if (auth_context) |
---|
106 | { |
---|
107 | krb5_auth_con_free(ssh_context, auth_context); |
---|
108 | auth_context = 0; |
---|
109 | } |
---|
110 | log_msg("Kerberos ticket authentication of user %.100s failed: %.100s", |
---|
111 | server_user, error_message(problem)); |
---|
112 | |
---|
113 | debug("Kerberos krb5_auth_con_genaddrs (%.100s).", error_message(problem)); |
---|
114 | packet_send_debug("Kerberos krb5_auth_con_genaddrs: %.100s", |
---|
115 | error_message(problem)); |
---|
116 | return 0; |
---|
117 | } |
---|
118 | problem = krb5_rd_req(ssh_context, &auth_context, auth, |
---|
119 | NULL, NULL, NULL, &ticket); |
---|
120 | if (problem) |
---|
121 | { |
---|
122 | if (auth_context) |
---|
123 | { |
---|
124 | krb5_auth_con_free(ssh_context, auth_context); |
---|
125 | auth_context = 0; |
---|
126 | } |
---|
127 | log_msg("Kerberos ticket authentication of user %.100s failed: %.100s", |
---|
128 | server_user, error_message(problem)); |
---|
129 | |
---|
130 | debug("Kerberos V5 rd_req failed (%.100s).", error_message(problem)); |
---|
131 | packet_send_debug("Kerberos V5 krb5_rd_req: %.100s", error_message(problem)); |
---|
132 | return 0; |
---|
133 | } |
---|
134 | |
---|
135 | /* Verify from ticket that the server used was of the form host/system */ |
---|
136 | problem = krb5_unparse_name(ssh_context, ticket->server, &server); |
---|
137 | if (problem) |
---|
138 | { |
---|
139 | krb5_free_ticket(ssh_context, ticket); |
---|
140 | log_msg("Kerberos ticket authentication of user %.100s failed: %.100s", |
---|
141 | server_user, error_message(problem)); |
---|
142 | |
---|
143 | debug("Kerberos krb5_unparse_name failed (%.100s).", error_message(problem)); |
---|
144 | packet_send_debug("Kerberos krb5_unparse_name: %.100s", |
---|
145 | error_message(problem)); |
---|
146 | return 0; |
---|
147 | } |
---|
148 | if (strncmp(server, "host/", strlen("host/"))) |
---|
149 | { |
---|
150 | krb5_free_ticket(ssh_context, ticket); |
---|
151 | log_msg("Kerberos ticket authentication of user %.100s failed: invalid service name (%.100s)", |
---|
152 | server_user, server); |
---|
153 | |
---|
154 | debug("Kerberos invalid service name (%.100s).", server); |
---|
155 | packet_send_debug("Kerberos invalid service name (%.100s).", server); |
---|
156 | krb5_xfree(server); |
---|
157 | return 0; |
---|
158 | } |
---|
159 | krb5_xfree(server); |
---|
160 | |
---|
161 | /* Extract the users name from the ticket client principal */ |
---|
162 | problem = krb5_copy_principal(ssh_context, ticket->enc_part2->client, |
---|
163 | &tkt_client); |
---|
164 | |
---|
165 | krb5_free_ticket(ssh_context, ticket); |
---|
166 | |
---|
167 | if (problem) |
---|
168 | { |
---|
169 | log_msg("Kerberos ticket authentication of user %.100s failed: %.100s", |
---|
170 | server_user, error_message(problem)); |
---|
171 | debug("Kerberos krb5_copy_principal failed (%.100s).", |
---|
172 | error_message(problem)); |
---|
173 | packet_send_debug("Kerberos krb5_copy_principal: %.100s", |
---|
174 | error_message(problem)); |
---|
175 | return 0; |
---|
176 | } |
---|
177 | *client = tkt_client; |
---|
178 | |
---|
179 | /* Make the reply - so that mutual authentication can be done */ |
---|
180 | if ((problem = krb5_mk_rep(ssh_context, auth_context, &reply))) |
---|
181 | { |
---|
182 | log_msg("Kerberos ticket authentication of user %.100s failed: %.100s", |
---|
183 | server_user, error_message(problem)); |
---|
184 | debug("Kerberos krb5_mk_rep failed (%.100s).", |
---|
185 | error_message(problem)); |
---|
186 | packet_send_debug("Kerberos krb5_mk_rep failed: %.100s", |
---|
187 | error_message(problem)); |
---|
188 | return 0; |
---|
189 | } |
---|
190 | |
---|
191 | packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); |
---|
192 | packet_put_string((char *) reply.data, reply.length); |
---|
193 | packet_send(); |
---|
194 | packet_write_wait(); |
---|
195 | krb5_xfree(reply.data); |
---|
196 | return 1; |
---|
197 | } |
---|
198 | #endif /* KRB5 */ |
---|
199 | #endif /* KERBEROS */ |
---|
200 | |
---|
201 | #ifdef KERBEROS_TGT_PASSING |
---|
202 | #if defined (KRB5) |
---|
203 | int auth_kerberos_tgt( char *server_user, krb5_data *krb5data) |
---|
204 | { |
---|
205 | krb5_creds **creds; |
---|
206 | krb5_error_code retval; |
---|
207 | static char ccname[512], tktname[512]; |
---|
208 | krb5_ccache ccache = NULL; |
---|
209 | struct passwd *pwd; |
---|
210 | extern char *ticket; |
---|
211 | static krb5_principal rcache_server = 0; |
---|
212 | static krb5_rcache rcache; |
---|
213 | struct sockaddr_in local, foreign; |
---|
214 | krb5_address *local_addr, *remote_addr; |
---|
215 | int s; |
---|
216 | krb5_data *realm; |
---|
217 | krb5_creds increds, *v5creds; |
---|
218 | CREDENTIALS v4creds; |
---|
219 | |
---|
220 | if (!(pwd = (struct passwd *) getpwnam(server_user))) |
---|
221 | { |
---|
222 | log_msg("Kerberos V5 tgt rejected for user %.100s", server_user); |
---|
223 | packet_send_debug("Kerberos V5 tgt rejected for %.100s", server_user); |
---|
224 | packet_start(SSH_SMSG_FAILURE); |
---|
225 | packet_send(); |
---|
226 | packet_write_wait(); |
---|
227 | return 0; |
---|
228 | } |
---|
229 | |
---|
230 | if (!auth_context) |
---|
231 | { |
---|
232 | if (!rcache_server) |
---|
233 | krb5_parse_name(ssh_context,"sshd", &rcache_server); |
---|
234 | krb5_auth_con_init(ssh_context, &auth_context); |
---|
235 | |
---|
236 | /* Set the addresses for local and remote systems, and replay |
---|
237 | cache */ |
---|
238 | |
---|
239 | /* GDM : We need to establish the local addresses and remote addresses |
---|
240 | within auth_context, in order to to TGT forwarding. Normally |
---|
241 | when the authentication credentials are passed, these are |
---|
242 | established then, however in SSH the forwarded TGT is |
---|
243 | passed prior to authentication. (Needed by AFS) */ |
---|
244 | |
---|
245 | s = packet_get_connection_in(); |
---|
246 | krb5_auth_con_genaddrs(ssh_context, auth_context, s, |
---|
247 | KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | |
---|
248 | KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); |
---|
249 | krb5_get_server_rcache(ssh_context, |
---|
250 | krb5_princ_component(context, rcache_server, 0), |
---|
251 | &rcache); |
---|
252 | krb5_auth_con_setrcache( ssh_context, auth_context, rcache); |
---|
253 | |
---|
254 | } |
---|
255 | |
---|
256 | if (retval = krb5_rd_cred(ssh_context, auth_context, krb5data, &creds, NULL)) |
---|
257 | { |
---|
258 | log_msg("Kerberos V5 tgt rejected for user %.100s : %.100s", server_user, |
---|
259 | error_message(retval)); |
---|
260 | packet_send_debug("Kerberos V5 tgt rejected for %.100s : %.100s", |
---|
261 | server_user, |
---|
262 | error_message(retval)); |
---|
263 | packet_start(SSH_SMSG_FAILURE); |
---|
264 | packet_send(); |
---|
265 | packet_write_wait(); |
---|
266 | return 0; |
---|
267 | } |
---|
268 | |
---|
269 | sprintf(ccname, "FILE:/tmp/krb5cc_p%d", getpid()); |
---|
270 | |
---|
271 | if (retval = krb5_cc_resolve(ssh_context, ccname, &ccache)) |
---|
272 | goto errout; |
---|
273 | |
---|
274 | if (retval = krb5_cc_initialize(ssh_context, ccache, (*creds)->client)) |
---|
275 | goto errout; |
---|
276 | |
---|
277 | if (retval = krb5_cc_store_cred(ssh_context, ccache, *creds)) |
---|
278 | goto errout; |
---|
279 | |
---|
280 | ticket = xmalloc(strlen(ccname) + 1); |
---|
281 | (void) sprintf(ticket, "%.100s", ccname); |
---|
282 | |
---|
283 | /* Now try to get krb4 tickets */ |
---|
284 | krb524_init_ets(ssh_context); |
---|
285 | realm = krb5_princ_realm(ssh_context, (*creds)->client); |
---|
286 | memset(&increds, 0, sizeof(increds)); |
---|
287 | if (retval = krb5_build_principal_ext(ssh_context, &(increds.server), |
---|
288 | realm->length, realm->data, 6, |
---|
289 | "krbtgt", realm->length, realm->data, |
---|
290 | NULL)) |
---|
291 | goto errout2; |
---|
292 | |
---|
293 | increds.client = (*creds)->client; |
---|
294 | increds.times.endtime = 0; |
---|
295 | increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; |
---|
296 | if (retval = krb5_get_credentials(ssh_context, 0, ccache, &increds, |
---|
297 | &v5creds)) |
---|
298 | goto errout2; |
---|
299 | |
---|
300 | if (retval = krb524_convert_creds_kdc(ssh_context, v5creds, &v4creds)) |
---|
301 | goto errout2; |
---|
302 | |
---|
303 | sprintf(tktname, "KRBTKFILE=/tmp/tkt_p%d", getpid()); |
---|
304 | putenv(xstrdup(tktname)); |
---|
305 | if (retval = in_tkt(v4creds.pname, v4creds.pinst)) |
---|
306 | goto errout2; |
---|
307 | |
---|
308 | if (retval = krb_save_credentials(v4creds.service, v4creds.instance, |
---|
309 | v4creds.realm, v4creds.session, |
---|
310 | v4creds.lifetime, v4creds.kvno, |
---|
311 | &(v4creds.ticket_st), v4creds.issue_date)) |
---|
312 | goto errout2; |
---|
313 | |
---|
314 | /* Successful */ |
---|
315 | packet_start(SSH_SMSG_SUCCESS); |
---|
316 | packet_send(); |
---|
317 | packet_write_wait(); |
---|
318 | havecred = 1; |
---|
319 | atexit(krb_cleanup); |
---|
320 | return 1; |
---|
321 | |
---|
322 | errout3: |
---|
323 | dest_tkt(); |
---|
324 | errout2: |
---|
325 | krb5_cc_destroy(ssh_context, ccache); |
---|
326 | errout: |
---|
327 | krb5_free_tgt_creds(ssh_context, creds); |
---|
328 | log_msg("Kerberos V5 tgt rejected for user %.100s :%.100s", server_user, |
---|
329 | error_message(retval)); |
---|
330 | packet_send_debug("Kerberos V5 tgt rejected for %.100s : %.100s", server_user, |
---|
331 | error_message(retval)); |
---|
332 | packet_start(SSH_SMSG_FAILURE); |
---|
333 | packet_send(); |
---|
334 | packet_write_wait(); |
---|
335 | return 0; |
---|
336 | |
---|
337 | } |
---|
338 | #endif /* KRB5 */ |
---|
339 | #endif /* KERBEROS_TGT_PASSING */ |
---|
340 | |
---|