]>
Commit | Line | Data |
---|---|---|
b59afbfe | 1 | /* |
23987cb8 | 2 | * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. |
b59afbfe | 3 | * |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | */ | |
24 | ||
25 | #include "includes.h" | |
26 | ||
27 | #ifdef GSSAPI | |
28 | #include "auth.h" | |
29 | #include "ssh2.h" | |
30 | #include "ssh1.h" | |
31 | #include "xmalloc.h" | |
32 | #include "log.h" | |
33 | #include "dispatch.h" | |
34 | #include "servconf.h" | |
35 | #include "compat.h" | |
36 | #include "packet.h" | |
37 | #include "monitor_wrap.h" | |
38 | ||
39 | #include "ssh-gss.h" | |
40 | ||
41 | extern ServerOptions options; | |
23987cb8 | 42 | unsigned char ssh1_key_digest[16]; |
b59afbfe | 43 | |
44 | static int | |
45 | userauth_external(Authctxt *authctxt) | |
46 | { | |
47 | packet_check_eom(); | |
48 | ||
49 | return(PRIVSEP(ssh_gssapi_userok(authctxt->user))); | |
50 | } | |
51 | ||
23987cb8 | 52 | static void ssh_gssapi_userauth_error(Gssctxt *ctxt); |
b59afbfe | 53 | static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); |
54 | static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); | |
e23e524c | 55 | static void input_gssapi_errtok(int, u_int32_t, void *); |
b59afbfe | 56 | |
57 | /* We only support those mechanisms that we know about (ie ones that we know | |
58 | * how to check local user kuserok and the like | |
59 | */ | |
60 | static int | |
61 | userauth_gssapi(Authctxt *authctxt) | |
62 | { | |
63 | gss_OID_desc oid= {0,NULL}; | |
64 | Gssctxt *ctxt = NULL; | |
65 | int mechs; | |
66 | gss_OID_set supported; | |
67 | int present; | |
68 | OM_uint32 ms; | |
69 | u_int len; | |
23987cb8 | 70 | char * doid = NULL; |
b59afbfe | 71 | |
72 | if (!authctxt->valid || authctxt->user == NULL) | |
73 | return 0; | |
74 | ||
75 | if (datafellows & SSH_OLD_GSSAPI) { | |
76 | debug("Early drafts of GSSAPI userauth not supported"); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | mechs=packet_get_int(); | |
81 | if (mechs==0) { | |
82 | debug("Mechanism negotiation is not supported"); | |
83 | return 0; | |
84 | } | |
85 | ||
86 | ssh_gssapi_supported_oids(&supported); | |
87 | do { | |
b59afbfe | 88 | mechs--; |
23987cb8 | 89 | |
90 | if (doid) | |
91 | xfree(doid); | |
92 | ||
93 | debug("Trying to get OID string"); | |
94 | doid = packet_get_string(&len); | |
95 | debug("Got string"); | |
96 | ||
e23e524c | 97 | if (doid[0]!=0x06 || doid[1]!=len-2) { |
98 | log("Mechanism OID received using the old encoding form"); | |
99 | oid.elements = doid; | |
100 | oid.length = len; | |
101 | } else { | |
102 | oid.elements = doid + 2; | |
103 | oid.length = len - 2; | |
104 | } | |
105 | gss_test_oid_set_member(&ms, &oid, supported, &present); | |
b59afbfe | 106 | } while (mechs>0 && !present); |
107 | ||
108 | if (!present) { | |
23987cb8 | 109 | xfree(doid); |
b59afbfe | 110 | return(0); |
111 | } | |
112 | ||
23987cb8 | 113 | if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt,&oid)))) { |
114 | ssh_gssapi_userauth_error(ctxt); | |
b59afbfe | 115 | return(0); |
23987cb8 | 116 | } |
b59afbfe | 117 | |
118 | authctxt->methoddata=(void *)ctxt; | |
119 | ||
120 | /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ | |
121 | ||
23987cb8 | 122 | if (!compat20) { |
123 | ||
124 | packet_start(SSH_SMSG_AUTH_GSSAPI_RESPONSE); | |
125 | packet_put_string(oid.elements,oid.length); | |
126 | ||
127 | } else { | |
128 | ||
129 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); | |
130 | ||
e23e524c | 131 | /* Just return whatever they sent */ |
132 | packet_put_string(doid,len); | |
23987cb8 | 133 | |
134 | } /* !compat20 */ | |
135 | ||
b59afbfe | 136 | packet_send(); |
137 | packet_write_wait(); | |
23987cb8 | 138 | xfree(doid); |
b59afbfe | 139 | |
140 | if (!compat20) | |
141 | dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, | |
142 | &input_gssapi_token); | |
143 | else | |
23987cb8 | 144 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, |
145 | &input_gssapi_token); | |
e23e524c | 146 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, |
147 | &input_gssapi_errtok); | |
b59afbfe | 148 | authctxt->postponed = 1; |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static void | |
154 | input_gssapi_token(int type, u_int32_t plen, void *ctxt) | |
155 | { | |
156 | Authctxt *authctxt = ctxt; | |
157 | Gssctxt *gssctxt; | |
158 | gss_buffer_desc send_tok,recv_tok; | |
159 | OM_uint32 maj_status, min_status; | |
d7590ddf | 160 | u_int len; |
b59afbfe | 161 | |
162 | if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) | |
163 | fatal("No authentication or GSSAPI context"); | |
164 | ||
165 | gssctxt=authctxt->methoddata; | |
007914b3 | 166 | recv_tok.value=packet_get_string(&len); |
d7590ddf | 167 | recv_tok.length=len; /* u_int vs. size_t */ |
b59afbfe | 168 | |
169 | maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, | |
e23e524c | 170 | &send_tok, NULL)); |
b59afbfe | 171 | packet_check_eom(); |
23987cb8 | 172 | |
705f2ba4 | 173 | if (GSS_ERROR(maj_status)) { |
e23e524c | 174 | ssh_gssapi_userauth_error(gssctxt); |
175 | if (send_tok.length != 0) { | |
176 | if (!compat20) | |
177 | packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN); | |
178 | else | |
179 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); | |
180 | packet_put_string(send_tok.value,send_tok.length); | |
181 | packet_send(); | |
182 | packet_write_wait(); | |
183 | } | |
705f2ba4 | 184 | authctxt->postponed = 0; |
185 | dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL); | |
186 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); | |
187 | userauth_finish(authctxt, 0, "gssapi"); | |
e23e524c | 188 | } else { |
189 | if (send_tok.length != 0) { | |
190 | if (!compat20) | |
191 | packet_start(SSH_MSG_AUTH_GSSAPI_TOKEN); | |
192 | else | |
193 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); | |
194 | packet_put_string(send_tok.value,send_tok.length); | |
195 | packet_send(); | |
196 | packet_write_wait(); | |
197 | } | |
198 | if (maj_status == GSS_S_COMPLETE) { | |
199 | dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL); | |
200 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL); | |
201 | if (!compat20) | |
202 | input_gssapi_exchange_complete(0, 0, ctxt); | |
203 | else | |
204 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, | |
205 | &input_gssapi_exchange_complete); | |
206 | } | |
705f2ba4 | 207 | } |
e23e524c | 208 | |
209 | gss_release_buffer(&min_status, &send_tok); | |
210 | } | |
23987cb8 | 211 | |
e23e524c | 212 | static void |
213 | input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) | |
214 | { | |
215 | Authctxt *authctxt = ctxt; | |
216 | Gssctxt *gssctxt; | |
217 | gss_buffer_desc send_tok,recv_tok; | |
218 | OM_uint32 maj_status; | |
219 | ||
220 | if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) | |
221 | fatal("No authentication or GSSAPI context"); | |
222 | ||
223 | gssctxt=authctxt->methoddata; | |
224 | recv_tok.value=packet_get_string(&recv_tok.length); | |
225 | ||
226 | /* Push the error token into GSSAPI to see what it says */ | |
227 | maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, | |
228 | &send_tok, NULL)); | |
229 | packet_check_eom(); | |
230 | ||
231 | /* We can't return anything to the client, even if we wanted to */ | |
232 | dispatch_set(SSH_MSG_AUTH_GSSAPI_TOKEN, NULL); | |
233 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); | |
234 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,NULL); | |
235 | ||
236 | /* The client will have already moved on to the next auth */ | |
237 | ||
b59afbfe | 238 | } |
239 | ||
240 | /* This is called when the client thinks we've completed authentication. | |
241 | * It should only be enabled in the dispatch handler by the function above, | |
242 | * which only enables it once the GSSAPI exchange is complete. | |
243 | */ | |
244 | ||
245 | static void | |
246 | input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) | |
247 | { | |
248 | Authctxt *authctxt = ctxt; | |
249 | Gssctxt *gssctxt; | |
250 | int authenticated; | |
251 | ||
252 | if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) | |
253 | fatal("No authentication or GSSAPI context"); | |
254 | ||
d13dfff4 | 255 | if ((strcmp(authctxt->user, "") == 0) && (authctxt->pw == NULL)) { |
256 | char *lname = NULL; | |
257 | PRIVSEP(ssh_gssapi_localname(&lname)); | |
258 | if (lname && lname[0] != '\0') { | |
259 | xfree(authctxt->user); | |
260 | authctxt->user = lname; | |
261 | debug("set username to %s from gssapi context", lname); | |
262 | authctxt->pw = PRIVSEP(getpwnamallow(authctxt->user)); | |
263 | } else { | |
264 | debug("failed to set username from gssapi context"); | |
265 | } | |
266 | } | |
267 | if (authctxt->pw) { | |
268 | #ifdef USE_PAM | |
269 | PRIVSEP(start_pam(authctxt->pw->pw_name)); | |
270 | #endif | |
271 | } else { | |
272 | authctxt->valid = 0; | |
273 | authenticated = 0; | |
274 | goto finish; | |
275 | } | |
276 | ||
b59afbfe | 277 | gssctxt=authctxt->methoddata; |
23987cb8 | 278 | |
b59afbfe | 279 | /* ssh1 needs to exchange the hash of the keys */ |
280 | if (!compat20) { | |
281 | ||
282 | OM_uint32 min_status; | |
283 | gss_buffer_desc dummy, msg_tok; | |
284 | ||
285 | /* ssh1 wraps the keys, in the monitor */ | |
286 | ||
287 | dummy.value=malloc(sizeof(ssh1_key_digest)); | |
288 | memcpy(dummy.value,ssh1_key_digest,sizeof(ssh1_key_digest)); | |
289 | dummy.length=sizeof(ssh1_key_digest); | |
290 | if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(gssctxt,&dummy,&msg_tok)))) | |
291 | fatal("Couldn't wrap keys"); | |
292 | ||
293 | packet_start(SSH_SMSG_AUTH_GSSAPI_HASH); | |
294 | packet_put_string((char *)msg_tok.value,msg_tok.length); | |
295 | packet_send(); | |
296 | packet_write_wait(); | |
297 | gss_release_buffer(&min_status,&msg_tok); | |
298 | } | |
299 | ||
300 | ||
301 | /* We don't need to check the status, because the stored credentials | |
302 | * which userok uses are only populated once the context init step | |
303 | * has returned complete. | |
304 | */ | |
305 | ||
306 | authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); | |
307 | ||
d13dfff4 | 308 | finish: |
b59afbfe | 309 | authctxt->postponed = 0; |
310 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); | |
e23e524c | 311 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); |
b59afbfe | 312 | dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); |
313 | userauth_finish(authctxt, authenticated, "gssapi"); | |
314 | } | |
315 | ||
23987cb8 | 316 | static void ssh_gssapi_userauth_error(Gssctxt *ctxt) { |
317 | char *errstr; | |
318 | OM_uint32 maj,min; | |
319 | ||
320 | errstr=PRIVSEP(ssh_gssapi_last_error(ctxt,&maj,&min)); | |
321 | if (errstr) { | |
e23e524c | 322 | if (!compat20) { |
323 | packet_send_debug(errstr); | |
324 | } else { | |
23987cb8 | 325 | packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); |
326 | packet_put_int(maj); | |
327 | packet_put_int(min); | |
328 | packet_put_cstring(errstr); | |
329 | packet_put_cstring(""); | |
330 | packet_send(); | |
331 | packet_write_wait(); | |
332 | xfree(errstr); | |
e23e524c | 333 | } |
23987cb8 | 334 | } |
335 | } | |
336 | ||
b59afbfe | 337 | Authmethod method_external = { |
338 | "external-keyx", | |
339 | userauth_external, | |
340 | &options.gss_authentication | |
341 | }; | |
342 | ||
343 | Authmethod method_gssapi = { | |
344 | "gssapi", | |
345 | userauth_gssapi, | |
346 | &options.gss_authentication | |
347 | }; | |
348 | ||
349 | #endif /* GSSAPI */ |