]>
Commit | Line | Data |
---|---|---|
5d0a7127 | 1 | /* |
2 | * Copyright (C) 1998-2000 Luke Howard. All rights reserved. | |
3 | * CONFIDENTIAL | |
4 | * $Id$ | |
5 | * | |
6 | * Implementation of GSS-API client side binding for SASL | |
7 | */ | |
8 | ||
9 | #ifdef _WIN32 | |
10 | #include <windows.h> | |
11 | #endif | |
12 | #include <stdio.h> | |
13 | #include <string.h> | |
14 | #include <sys/types.h> | |
15 | #ifndef _WIN32 | |
16 | #include <sys/uio.h> | |
cd9e6b16 | 17 | #include <sys/socket.h> |
5d0a7127 | 18 | #include <netinet/in.h> |
19 | #endif | |
20 | ||
cd9e6b16 | 21 | #include <arpa/nameser.h> |
22 | #include <resolv.h> | |
23 | #include <lber.h> | |
5d0a7127 | 24 | #include <krb5.h> |
25 | #include "ldap-int.h" | |
26 | #include "gssldap-int.h" | |
27 | ||
28 | #ifndef _WIN32 | |
29 | typedef gss_uint32 OM_uint32; | |
30 | #endif | |
31 | ||
cd9e6b16 | 32 | char ldap_domain_name[128]; |
33 | ||
34 | #ifndef T_SRV | |
35 | #define T_SRV 33 | |
36 | #endif | |
37 | #define LDAP_SERVICE "_ldap" | |
38 | #define TCP_PROTOCOL "_tcp" | |
39 | ||
40 | int locate_ldap_server(char *domain, char **server_name); | |
5d0a7127 | 41 | int ldap_delete_tickets(LDAP *ld, char *service_name); |
cd9e6b16 | 42 | static int negotiate_security_options(gssldap_client_state_t state, |
43 | int layer); | |
5d0a7127 | 44 | int ldap_reset_principal(LDAP *ld, char *service_name, gss_name_t target_name); |
cd9e6b16 | 45 | unsigned long gsssasl_pack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context, |
46 | gsssasl_security_negotiation_t inmask, | |
47 | size_t masklength, gss_buffer_t wrapped_tok); | |
48 | ||
5d0a7127 | 49 | int gsssasl_unpack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context, |
cd9e6b16 | 50 | gss_buffer_t wrapped_tok, |
51 | gsssasl_security_negotiation_t *outmask, | |
52 | size_t *masklength); | |
5d0a7127 | 53 | |
54 | #ifdef GSSSASL_DEBUG | |
55 | static int debug_ = 1; | |
56 | #define TRACE(x) do { if (debug_) fprintf(stderr, "%s\n", x); fflush(stderr); } while (0); | |
57 | #define LDAP_PERROR(ld, x) do { if (debug_) ldap_perror(ld, x); } while (0); | |
58 | #define LOG_STATUS(msg, min, maj) log_status(msg, min, maj) | |
59 | #else | |
60 | #define TRACE(x) | |
61 | #define LDAP_PERROR(ld, x) | |
62 | #define LOG_STATUS(msg, min, maj) | |
cd9e6b16 | 63 | #endif /* GSSSASL_DEBUG */ |
5d0a7127 | 64 | |
65 | #ifdef HACK_SERVICE_NAME | |
66 | char *__service = NULL; | |
67 | #endif | |
68 | ||
69 | /* | |
70 | * The read and write fns need to access the context in here and | |
71 | * there doesn't appear to be a way to pass this info to them. | |
72 | */ | |
73 | static gss_ctx_id_t security_context; | |
74 | static int security_layer = 0; | |
777bcce2 | 75 | static unsigned long security_token_size = 0; |
5d0a7127 | 76 | |
cd9e6b16 | 77 | LDAP_CALLBACK int LDAP_C ldap_gssapi_read(LBER_SOCKET sock, void *data, |
78 | int len) | |
79 | { | |
80 | OM_uint32 maj_stat; | |
81 | OM_uint32 min_stat; | |
82 | OM_uint32 rc; | |
83 | OM_uint32 pdulen; | |
84 | gss_buffer_desc recv_tok; | |
85 | gss_buffer_desc wrapped_tok; | |
777bcce2 | 86 | int count; |
cd9e6b16 | 87 | |
88 | if (security_layer & | |
89 | (GSSSASL_INTEGRITY_PROTECTION | GSSSASL_PRIVACY_PROTECTION)) | |
90 | { | |
91 | if (recv(sock, (char *)&pdulen, sizeof(pdulen), 0) != sizeof(pdulen)) | |
92 | return (0); | |
93 | wrapped_tok.length = ntohl(pdulen); | |
94 | #ifdef GSSSASL_DEBUG | |
95 | if (debug_) | |
96 | { | |
97 | fprintf(stderr, "Reading data of %d octets\n", | |
98 | wrapped_tok.length); | |
99 | } | |
100 | #endif /* GSSSASL_DEBUG */ | |
101 | if ((int)wrapped_tok.length > security_token_size) | |
102 | return (0); | |
103 | wrapped_tok.value = malloc(wrapped_tok.length); | |
104 | if (wrapped_tok.value == NULL) | |
105 | return (0); | |
777bcce2 | 106 | count = recv(sock, wrapped_tok.value, wrapped_tok.length, 0); |
107 | if (count != (int)wrapped_tok.length) | |
cd9e6b16 | 108 | { |
109 | gss_release_buffer(&rc, &wrapped_tok); | |
110 | return (0); | |
111 | } | |
112 | maj_stat = gss_unwrap(&min_stat, | |
113 | security_context, | |
114 | &wrapped_tok, | |
115 | &recv_tok, | |
116 | &rc, | |
117 | (gss_qop_t *) NULL); | |
118 | if (maj_stat != GSS_S_COMPLETE) | |
119 | { | |
120 | gss_release_buffer(&rc, &wrapped_tok); | |
121 | return maj_stat; | |
122 | } | |
123 | #ifdef GSSSASL_DEBUG | |
124 | if (debug_) | |
125 | { | |
126 | fprintf(stderr, "Got %d bytes of %s data\n", | |
127 | recv_tok.length, rc ? "private" : "signed"); | |
128 | } | |
129 | #endif /* GSSSASL_DEBUG */ | |
130 | if ((int)recv_tok.length > len) | |
131 | { | |
132 | gss_release_buffer(&rc, &recv_tok); | |
133 | gss_release_buffer(&rc, &wrapped_tok); | |
134 | return GSS_S_FAILURE; | |
135 | } | |
136 | memcpy(data, recv_tok.value, recv_tok.length); | |
137 | pdulen = recv_tok.length; | |
138 | gss_release_buffer(&rc, &recv_tok); | |
139 | gss_release_buffer(&rc, &wrapped_tok); | |
140 | ||
141 | return pdulen; | |
142 | } | |
143 | else | |
144 | return read(sock, data, len); | |
145 | } | |
146 | ||
147 | LDAP_CALLBACK int LDAP_C ldap_gssapi_write(LBER_SOCKET sock, const void *data, | |
148 | int len) | |
149 | { | |
150 | OM_uint32 maj_stat; | |
151 | OM_uint32 min_stat; | |
152 | OM_uint32 pdulen; | |
153 | OM_uint32 rc; | |
154 | gss_buffer_desc send_tok; | |
155 | gss_buffer_desc wrapped_tok; | |
156 | unsigned char *ptr; | |
157 | ||
158 | if (security_layer & | |
159 | (GSSSASL_INTEGRITY_PROTECTION | GSSSASL_PRIVACY_PROTECTION)) | |
160 | { | |
161 | send_tok.length = len; | |
162 | send_tok.value = (void *) data; | |
163 | #ifdef GSSSASL_DEBUG | |
164 | if (debug_) | |
165 | { | |
166 | fprintf(stderr, "Sending %d bytes of data\n", | |
167 | len); | |
168 | } | |
169 | #endif /* GSSSASL_DEBUG */ | |
170 | maj_stat = gss_wrap(&min_stat, | |
171 | security_context, | |
172 | (security_layer & GSSSASL_PRIVACY_PROTECTION) ? 1 : 0, | |
173 | GSS_C_QOP_DEFAULT, | |
174 | &send_tok, | |
175 | &rc, | |
176 | &wrapped_tok); | |
177 | if (maj_stat != GSS_S_COMPLETE) | |
178 | return maj_stat; | |
179 | #ifdef GSSSASL_DEBUG | |
180 | if (debug_) | |
181 | { | |
182 | fprintf(stderr, "Sent %d bytes of %s data\n", | |
183 | wrapped_tok.length, rc ? "private" : "signed"); | |
184 | } | |
185 | #endif /* GSSSASL_DEBUG */ | |
186 | pdulen = htonl(wrapped_tok.length); | |
187 | ptr = calloc(1, sizeof(pdulen) + wrapped_tok.length); | |
188 | memcpy(ptr, &pdulen, sizeof(pdulen)); | |
189 | memcpy(&ptr[sizeof(pdulen)], wrapped_tok.value, wrapped_tok.length); | |
190 | rc = send(sock, ptr, sizeof(pdulen) + wrapped_tok.length, 0); | |
191 | free(ptr); | |
192 | if (rc == (wrapped_tok.length + sizeof(pdulen))) | |
193 | rc = len; | |
194 | gss_release_buffer(&maj_stat, &wrapped_tok); | |
195 | return rc; | |
196 | } | |
197 | else | |
198 | return send(sock, data, len, 0); | |
199 | } | |
200 | ||
5d0a7127 | 201 | #ifdef GSSSASL_DEBUG |
202 | /* | |
203 | * Dump the token to stderr | |
204 | */ | |
205 | static void print_token(gss_buffer_t tok) | |
206 | { | |
207 | int i; | |
208 | unsigned char *p = tok->value; | |
cd9e6b16 | 209 | |
5d0a7127 | 210 | for (i = 0; i < (int)tok->length; i++, p++) |
211 | { | |
212 | fprintf(stderr, "%02x ", *p); | |
213 | if ((i % 16) == 15) | |
cd9e6b16 | 214 | { |
215 | fprintf(stderr, "\n"); | |
216 | } | |
5d0a7127 | 217 | } |
218 | fprintf(stderr, "\n"); | |
219 | fflush(stderr); | |
220 | return; | |
221 | } | |
222 | ||
223 | /* | |
224 | * Handle a GSS-API error | |
225 | */ | |
226 | static void log_status_impl(const char *reason, OM_uint32 res, int type) | |
227 | { | |
228 | OM_uint32 maj_stat, min_stat; | |
229 | gss_buffer_desc msg; | |
230 | OM_uint32 msg_ctx; | |
231 | ||
232 | msg_ctx = 0; | |
233 | while (1) | |
234 | { | |
cd9e6b16 | 235 | maj_stat = gss_display_status(&min_stat, |
236 | res, | |
237 | type, | |
238 | GSS_C_NULL_OID, | |
239 | &msg_ctx, | |
240 | &msg); | |
5d0a7127 | 241 | |
242 | if (debug_) | |
cd9e6b16 | 243 | { |
244 | fprintf(stderr, "ldap_adgssapi_bind: %s: %s\n", | |
245 | (char *) msg.value, | |
246 | reason); | |
247 | } | |
5d0a7127 | 248 | (void) gss_release_buffer(&min_stat, &msg); |
249 | if (!msg_ctx) | |
cd9e6b16 | 250 | break; |
5d0a7127 | 251 | } |
252 | return; | |
253 | } | |
254 | ||
255 | /* | |
256 | * Cover function to handle a GSS-API error | |
257 | */ | |
cd9e6b16 | 258 | static void log_status(const char *reason, OM_uint32 maj_stat, |
259 | OM_uint32 min_stat) | |
5d0a7127 | 260 | { |
261 | log_status_impl(reason, maj_stat, GSS_C_GSS_CODE); | |
262 | log_status_impl(reason, min_stat, GSS_C_MECH_CODE); | |
263 | return; | |
264 | } | |
cd9e6b16 | 265 | #endif /* GSSSASL_DEBUG */ |
5d0a7127 | 266 | |
267 | /* | |
268 | * Send a GSS-API token as part of a SASL BindRequest | |
269 | */ | |
270 | static int send_token(gssldap_client_state_t state, gss_buffer_t send_tok) | |
271 | { | |
272 | struct berval cred; | |
273 | ||
274 | TRACE("==> send_token"); | |
275 | ||
276 | cred.bv_val = send_tok->value; | |
277 | cred.bv_len = send_tok->length; | |
278 | ||
cd9e6b16 | 279 | if (ldap_sasl_bind(state->ld, |
280 | state->binddn, | |
281 | GSSAPI_SASL_NAME, | |
282 | &cred, | |
283 | NULL, | |
284 | NULL, | |
285 | &state->msgid) != LDAP_SUCCESS) | |
286 | { | |
5d0a7127 | 287 | LDAP_PERROR(state->ld, "send_token"); |
288 | TRACE("<== send_token"); | |
289 | return -1; | |
290 | } | |
291 | TRACE("<== send_token"); | |
292 | return 0; | |
293 | } | |
294 | ||
295 | /* | |
296 | * Parse the final result sent back from the server. | |
297 | */ | |
298 | static int parse_bind_result(gssldap_client_state_t state) | |
299 | { | |
300 | LDAPMessage *res = NULL, *msg = NULL; | |
301 | int rc; | |
302 | ||
303 | TRACE("==> parse_bind_result"); | |
304 | ||
cd9e6b16 | 305 | if (ldap_result(state->ld, |
306 | state->msgid, | |
307 | LDAP_MSG_ALL, | |
308 | NULL, | |
309 | &res) <= 0) | |
5d0a7127 | 310 | { |
311 | LDAP_PERROR(state->ld, "ldap_result"); | |
312 | TRACE("<== parse_bind_result"); | |
313 | return -1; | |
314 | } | |
cd9e6b16 | 315 | for (msg = ldap_first_message(state->ld, res); |
316 | msg != NULL; | |
317 | msg = ldap_next_message(state->ld, msg)) | |
5d0a7127 | 318 | { |
319 | if (ldap_msgtype(msg) == LDAP_RES_BIND) | |
cd9e6b16 | 320 | { |
321 | ldap_parse_result(state->ld, msg, &rc, NULL, NULL, NULL, NULL, 0); | |
322 | break; | |
323 | } | |
324 | } | |
5d0a7127 | 325 | |
326 | ldap_msgfree(res); | |
327 | state->msgid = -1; | |
cd9e6b16 | 328 | |
5d0a7127 | 329 | TRACE("<== parse_bind_result"); |
cd9e6b16 | 330 | |
5d0a7127 | 331 | if (rc == LDAP_SUCCESS) |
cd9e6b16 | 332 | { |
333 | return 0; | |
334 | } | |
5d0a7127 | 335 | state->rc = rc; |
336 | return -1; | |
337 | } | |
338 | ||
339 | /* | |
340 | * Receive a GSS-API token from a SASL BindResponse | |
341 | * The contents of recv_tok must be freed by the | |
342 | * caller. | |
343 | */ | |
344 | static int recv_token(gssldap_client_state_t state, gss_buffer_t recv_tok) | |
345 | { | |
346 | struct berval *servercred = NULL; | |
347 | LDAPMessage *res = NULL, *msg = NULL; | |
348 | int rc; | |
349 | ||
350 | TRACE("==> recv_token"); | |
351 | ||
352 | if (ldap_result(state->ld, state->msgid, LDAP_MSG_ALL, NULL, &res) <= 0) | |
353 | { | |
354 | LDAP_PERROR(state->ld, "ldap_result"); | |
355 | TRACE("<== recv_token"); | |
356 | return -1; | |
357 | } | |
358 | recv_tok->value = NULL; | |
359 | recv_tok->length = 0; | |
360 | ||
cd9e6b16 | 361 | for (msg = ldap_first_message(state->ld, res); |
362 | msg != NULL; | |
363 | msg = ldap_next_message(state->ld, msg)) | |
5d0a7127 | 364 | { |
cd9e6b16 | 365 | if (ldap_msgtype(msg) == LDAP_RES_BIND && servercred == NULL) |
366 | { | |
367 | rc = ldap_parse_sasl_bind_result(state->ld, | |
368 | msg, | |
369 | &servercred, | |
370 | 0); | |
371 | if (rc == LDAP_SUCCESS && servercred != NULL) | |
372 | { | |
373 | recv_tok->value = malloc(servercred->bv_len); | |
374 | if (recv_tok->value != NULL) | |
375 | { | |
376 | memcpy(recv_tok->value, servercred->bv_val, servercred->bv_len); | |
377 | recv_tok->length = servercred->bv_len; | |
378 | } | |
379 | break; | |
380 | } | |
381 | else | |
382 | { | |
383 | state->rc = rc; | |
384 | } | |
385 | } | |
386 | } | |
5d0a7127 | 387 | |
388 | ldap_msgfree(res); | |
389 | state->msgid = -1; | |
390 | TRACE("<== recv_token"); | |
391 | if (servercred != NULL) | |
392 | { | |
cd9e6b16 | 393 | nslberi_free(servercred); |
5d0a7127 | 394 | TRACE("<== recv_token"); |
395 | return 0; | |
cd9e6b16 | 396 | } |
5d0a7127 | 397 | if (state->rc == LDAP_SUCCESS) |
cd9e6b16 | 398 | { |
399 | state->rc = LDAP_OPERATIONS_ERROR; | |
400 | } | |
5d0a7127 | 401 | else |
cd9e6b16 | 402 | { |
403 | LDAP_PERROR(state->ld, "recv_token"); | |
404 | } | |
405 | ||
5d0a7127 | 406 | TRACE("<== recv_token"); |
407 | return -1; | |
408 | } | |
409 | ||
410 | /* | |
411 | * The client calls GSS_Init_sec_context, passing in 0 for | |
412 | * input_context_handle (initially) and a targ_name equal to output_name | |
413 | * from GSS_Import_Name called with input_name_type of | |
414 | * GSS_C_NT_HOSTBASED_SERVICE and input_name_string of | |
415 | * "service@hostname" where "service" is the service name specified in | |
416 | * the protocol's profile, and "hostname" is the fully qualified host | |
417 | * name of the server. The client then responds with the resulting | |
418 | * output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED, | |
419 | * then the client should expect the server to issue a token in a | |
420 | * subsequent challenge. The client must pass the token to another call | |
421 | * to GSS_Init_sec_context, repeating the actions in this paragraph. | |
422 | */ | |
423 | static int client_establish_context(LDAP *ld, gssldap_client_state_t state, | |
cd9e6b16 | 424 | char *service_name) |
5d0a7127 | 425 | { |
426 | gss_buffer_desc send_tok; | |
427 | gss_buffer_desc recv_tok; | |
428 | gss_buffer_desc *token_ptr; | |
cd9e6b16 | 429 | gss_name_t target_name; |
430 | OM_uint32 maj_stat; | |
431 | OM_uint32 min_stat; | |
432 | gss_OID oid = GSS_C_NULL_OID; | |
433 | OM_uint32 ret_flags; | |
5d0a7127 | 434 | |
435 | TRACE("==> client_establish_context"); | |
436 | ||
437 | memset(&recv_tok, '\0', sizeof(recv_tok)); | |
438 | send_tok.value = service_name; | |
439 | send_tok.length = strlen(send_tok.value) + 1; | |
440 | ||
cd9e6b16 | 441 | maj_stat = gss_import_name(&min_stat, |
442 | &send_tok, | |
443 | (gss_OID) gss_nt_service_name, | |
444 | &target_name); | |
5d0a7127 | 445 | if (ldap_reset_principal(ld, service_name, target_name)) |
446 | { | |
447 | TRACE("<== client_establish_context"); | |
448 | return -1; | |
449 | } | |
450 | ||
451 | token_ptr = GSS_C_NO_BUFFER; | |
452 | state->context = GSS_C_NO_CONTEXT; | |
cd9e6b16 | 453 | |
5d0a7127 | 454 | do |
455 | { | |
cd9e6b16 | 456 | maj_stat = gss_init_sec_context(&min_stat, |
457 | GSS_C_NO_CREDENTIAL, | |
458 | &state->context, | |
459 | target_name, | |
460 | oid, | |
461 | GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG, | |
462 | 0, | |
463 | NULL, | |
464 | token_ptr, | |
465 | NULL, | |
466 | &send_tok, | |
467 | &ret_flags, | |
468 | NULL); | |
5d0a7127 | 469 | |
470 | if (token_ptr != GSS_C_NO_BUFFER) | |
cd9e6b16 | 471 | (void) gss_release_buffer(&min_stat, &recv_tok); |
5d0a7127 | 472 | |
473 | if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) | |
cd9e6b16 | 474 | { |
475 | LOG_STATUS("initializing context", maj_stat, min_stat); | |
476 | (void) gss_release_name(&min_stat, &target_name); | |
477 | TRACE("<== client_establish_context"); | |
478 | return -1; | |
479 | } | |
5d0a7127 | 480 | #ifdef GSSSASL_DEBUG |
481 | if (debug_) | |
cd9e6b16 | 482 | { |
483 | fprintf(stderr, "Sending init_sec_context token (size=%d)...\n", | |
484 | send_tok.length); | |
485 | fflush(stderr); | |
486 | print_token(&send_tok); | |
487 | } | |
5d0a7127 | 488 | #endif |
489 | if (send_token(state, &send_tok) < 0) | |
cd9e6b16 | 490 | { |
491 | TRACE("Send_token failed"); | |
492 | (void) gss_release_buffer(&min_stat, &send_tok); | |
493 | (void) gss_release_name(&min_stat, &target_name); | |
494 | TRACE("<== client_establish_context"); | |
495 | return -1; | |
496 | } | |
5d0a7127 | 497 | (void) gss_release_buffer(&min_stat, &send_tok); |
498 | if (maj_stat == GSS_S_CONTINUE_NEEDED) | |
cd9e6b16 | 499 | { |
500 | TRACE("continue needed..."); | |
501 | if (recv_token(state, &recv_tok) < 0) | |
502 | { | |
503 | TRACE("recv_token failed"); | |
504 | (void) gss_release_name(&min_stat, &target_name); | |
505 | TRACE("<== client_establish_context"); | |
506 | return -1; | |
507 | } | |
5d0a7127 | 508 | #ifdef GSSSASL_DEBUG |
cd9e6b16 | 509 | if (debug_) |
510 | { | |
511 | fprintf(stderr, "Received token (size=%d)...\n", | |
512 | recv_tok.length); | |
513 | fflush(stderr); | |
514 | print_token(&recv_tok); | |
515 | } | |
5d0a7127 | 516 | #endif |
cd9e6b16 | 517 | token_ptr = &recv_tok; |
518 | } | |
5d0a7127 | 519 | } while (maj_stat == GSS_S_CONTINUE_NEEDED); |
520 | ||
521 | (void) gss_release_name(&min_stat, &target_name); | |
cd9e6b16 | 522 | |
5d0a7127 | 523 | TRACE("<== client_establish_context"); |
524 | return 0; | |
525 | } | |
526 | ||
527 | /* | |
528 | * When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes | |
529 | * the following actions: If the last call to GSS_Init_sec_context | |
530 | * returned an output_token, then the client responds with the | |
531 | * output_token, otherwise the client responds with no data. The client | |
532 | * should then expect the server to issue a token in a subsequent | |
533 | * challenge. The client passes this token to GSS_Unwrap and interprets | |
534 | * the first octet of resulting cleartext as a bit-mask specifying the | |
535 | * security layers supported by the server and the second through fourth | |
536 | * octets as the maximum size output_message to send to the server. The | |
537 | * client then constructs data, with the first octet containing the | |
538 | * bit-mask specifying the selected security layer, the second through | |
539 | * fourth octets containing in network byte order the maximum size | |
540 | * output_message the client is able to receive, and the remaining | |
541 | * octets containing the authorization identity. The client passes the | |
542 | * data to GSS_Wrap with conf_flag set to FALSE, and responds with the | |
543 | * generated output_message. The client can then consider the server | |
544 | * authenticated. | |
545 | */ | |
cd9e6b16 | 546 | static int negotiate_security_options(gssldap_client_state_t state, |
547 | int layer) | |
5d0a7127 | 548 | { |
cd9e6b16 | 549 | OM_uint32 maj_stat; |
550 | OM_uint32 min_stat; | |
5d0a7127 | 551 | gss_buffer_desc recv_tok; |
552 | gss_buffer_desc send_tok; | |
cd9e6b16 | 553 | OM_uint32 rc; |
777bcce2 | 554 | OM_uint32 ret; |
cd9e6b16 | 555 | size_t mask_length; |
556 | gsssasl_security_negotiation_t send_mask; | |
557 | gsssasl_security_negotiation_t recv_mask; | |
558 | ||
5d0a7127 | 559 | TRACE("==> negotiate_security_options"); |
cd9e6b16 | 560 | |
5d0a7127 | 561 | memset(&send_tok, '\0', sizeof(send_tok)); |
562 | memset(&recv_tok, '\0', sizeof(recv_tok)); | |
563 | if ((rc = recv_token(state, &recv_tok)) < 0) | |
564 | { | |
565 | TRACE("<== negotiate_security_options (recv_token failed)"); | |
566 | return rc; | |
567 | } | |
568 | #ifdef GSSSASL_DEBUG | |
569 | if (debug_) | |
cd9e6b16 | 570 | { |
571 | fprintf(stderr, "Received token (size=%d)...\n", | |
572 | recv_tok.length); | |
573 | fflush(stderr); | |
574 | print_token(&recv_tok); | |
5d0a7127 | 575 | } |
cd9e6b16 | 576 | #endif /* GSSSASL_DEBUG */ |
577 | ||
578 | maj_stat = gsssasl_unpack_security_token(&min_stat, | |
579 | state->context, | |
580 | &recv_tok, | |
581 | &recv_mask, | |
582 | &mask_length); | |
583 | ||
584 | if (maj_stat != GSS_S_COMPLETE) | |
585 | { | |
586 | LOG_STATUS("unpacking security negotiation token", | |
587 | maj_stat, | |
588 | min_stat); | |
589 | TRACE("<== negotiate_security_options (unpack failed)"); | |
590 | return -1; | |
591 | } | |
5d0a7127 | 592 | #ifdef GSSSASL_DEBUG |
cd9e6b16 | 593 | if (debug_) |
594 | { | |
595 | fprintf(stderr, "Received security token level %d size %d\n", | |
596 | recv_mask->security_layer, | |
597 | recv_mask->token_size); | |
5d0a7127 | 598 | } |
599 | #endif | |
cd9e6b16 | 600 | if ((~recv_mask->security_layer) & layer) |
601 | { | |
602 | free(recv_mask); | |
603 | TRACE("<== negotiate_security_options (unsupported security layer)"); | |
604 | return -1; | |
5d0a7127 | 605 | } |
777bcce2 | 606 | mask_length = sizeof(recv_mask); |
cd9e6b16 | 607 | send_mask = NSLDAPI_MALLOC(mask_length); |
608 | if (send_mask == NULL) | |
609 | { | |
610 | free(recv_mask); | |
611 | TRACE("<== negotiate_security_options (malloc failed)"); | |
612 | return -1; | |
613 | } | |
777bcce2 | 614 | memset(send_mask, '\0', mask_length); |
cd9e6b16 | 615 | send_mask->security_layer = layer; |
616 | send_mask->token_size = recv_mask->token_size; | |
777bcce2 | 617 | memcpy(send_mask->identity, "", strlen("")); |
cd9e6b16 | 618 | free(recv_mask); |
619 | ||
5d0a7127 | 620 | #ifdef GSSSASL_DEBUG |
cd9e6b16 | 621 | if (debug_) |
622 | { | |
623 | fprintf(stderr, "Sending security token level %d size %d\n", | |
624 | send_mask->security_layer, | |
625 | send_mask->token_size); | |
626 | } | |
5d0a7127 | 627 | #endif |
cd9e6b16 | 628 | maj_stat = gsssasl_pack_security_token(&min_stat, |
629 | state->context, | |
630 | send_mask, | |
631 | mask_length, | |
632 | &send_tok); | |
633 | if (maj_stat != GSS_S_COMPLETE) | |
634 | { | |
635 | LOG_STATUS("packing security negotiation token", maj_stat, min_stat); | |
636 | NSLDAPI_FREE(send_mask); | |
637 | TRACE("<== negotiate_security_options (pack failed)"); | |
638 | return -1; | |
639 | } | |
640 | if ((rc = send_token(state, &send_tok)) < 0) | |
641 | { | |
642 | NSLDAPI_FREE(send_mask); | |
643 | TRACE("<== negotiate_security_options (send_token failed)"); | |
644 | return rc; | |
645 | } | |
646 | rc = parse_bind_result(state); | |
647 | ||
777bcce2 | 648 | ret = rc; |
cd9e6b16 | 649 | if (rc == 0) |
650 | { | |
651 | security_context = state->context; | |
652 | security_layer = layer; | |
777bcce2 | 653 | security_token_size = ntohl(send_mask->token_size); |
654 | #ifdef _WIN32 | |
655 | security_token_size >>= 8; | |
656 | #endif | |
5d0a7127 | 657 | } |
5d0a7127 | 658 | |
cd9e6b16 | 659 | NSLDAPI_FREE(send_mask); |
660 | if (send_tok.value != NULL) | |
661 | gss_release_buffer(&rc, &send_tok); | |
662 | if (recv_tok.value != NULL) | |
663 | gss_release_buffer(&rc, &recv_tok); | |
664 | TRACE("<== negotiate_security_options"); | |
777bcce2 | 665 | return ret; |
5d0a7127 | 666 | } |
667 | ||
668 | #ifdef GSSSASL_DEBUG | |
669 | /* | |
670 | * Temporary function to enable debugging | |
671 | */ | |
672 | LDAP_API(int) | |
673 | LDAP_CALL ldap_gssapi_debug(int on) | |
674 | { | |
675 | int old = debug_; | |
676 | debug_ = on; | |
677 | return old; | |
678 | } | |
cd9e6b16 | 679 | #endif /* GSSSASL_DEBUG */ |
5d0a7127 | 680 | |
681 | /* | |
682 | * Public function for doing a GSS-API SASL bind | |
683 | */ | |
684 | LDAP_API(int) | |
685 | LDAP_CALL ldap_adgssapi_bind(LDAP *ld, const char *who, int layer) | |
686 | { | |
687 | gssldap_client_state_desc state; | |
cd9e6b16 | 688 | char *service_name; |
689 | OM_uint32 min_stat; | |
690 | int rc; | |
691 | int i; | |
692 | struct ldap_io_fns iofns; | |
777bcce2 | 693 | char *realm; |
5d0a7127 | 694 | |
695 | if (!NSLDAPI_VALID_LDAP_POINTER(ld) || ld->ld_defhost == NULL) | |
cd9e6b16 | 696 | { |
697 | return -1; | |
698 | } | |
699 | ||
5d0a7127 | 700 | for (i = 0; i < (int)strlen(ld->ld_defhost); i++) |
701 | ld->ld_defhost[i] = toupper(ld->ld_defhost[i]); | |
702 | ||
cd9e6b16 | 703 | service_name = |
704 | NSLDAPI_MALLOC(sizeof(GSSAPI_LDAP_SERVICE_NAME "@") + strlen(ld->ld_defhost)); | |
5d0a7127 | 705 | if (service_name == NULL) |
cd9e6b16 | 706 | { |
707 | return -1; | |
708 | } | |
5d0a7127 | 709 | strcpy(service_name, GSSAPI_LDAP_SERVICE_NAME "@"); |
777bcce2 | 710 | realm = strdup(ldap_domain_name); |
711 | for (i = 0; i < (int)strlen(realm); i++) | |
712 | realm[i] = toupper(realm[i]); | |
713 | strcat(service_name, realm); | |
714 | free(realm); | |
5d0a7127 | 715 | |
716 | #ifdef GSSSASL_DEBUG | |
cd9e6b16 | 717 | if (debug_) |
718 | { | |
719 | fprintf(stderr, "LDAP service name: %s\n", service_name); | |
720 | fflush(stderr); | |
721 | } | |
5d0a7127 | 722 | #endif |
723 | ||
cd9e6b16 | 724 | state.msgid = -1; |
725 | state.ld = ld; | |
726 | state.binddn = who; | |
727 | state.context = GSS_C_NO_CONTEXT; | |
728 | state.rc = LDAP_OPERATIONS_ERROR; | |
5d0a7127 | 729 | |
cd9e6b16 | 730 | rc = client_establish_context(ld, &state, service_name); |
731 | if (rc == 0) | |
732 | { | |
5d0a7127 | 733 | rc = negotiate_security_options(&state, layer); |
cd9e6b16 | 734 | } |
735 | if (rc == 0) | |
736 | { | |
5d0a7127 | 737 | #ifdef GSSSASL_DEBUG |
cd9e6b16 | 738 | gss_buffer_desc tname; |
739 | gss_buffer_desc sname; | |
740 | gss_name_t targ_name; | |
741 | gss_name_t src_name; | |
742 | OM_uint32 context_flags; | |
743 | OM_uint32 lifetime; | |
744 | OM_uint32 maj_stat; | |
745 | int is_open; | |
746 | int is_local; | |
747 | gss_OID mechanism; | |
748 | gss_OID name_type; | |
749 | ||
750 | maj_stat = gss_inquire_context(&min_stat, | |
751 | state.context, | |
752 | &src_name, | |
753 | &targ_name, | |
754 | &lifetime, | |
755 | &mechanism, | |
756 | &context_flags, | |
757 | &is_local, | |
758 | &is_open); | |
759 | if (maj_stat != GSS_S_COMPLETE) | |
760 | { | |
761 | LOG_STATUS("inquiring context", maj_stat, min_stat); | |
762 | return LDAP_OPERATIONS_ERROR; | |
763 | } | |
764 | maj_stat = gss_display_name(&min_stat, | |
765 | src_name, | |
766 | &sname, | |
767 | &name_type); | |
768 | if (maj_stat != GSS_S_COMPLETE) | |
769 | { | |
770 | LOG_STATUS("displaying source name", maj_stat, min_stat); | |
771 | return LDAP_OPERATIONS_ERROR; | |
772 | } | |
773 | maj_stat = gss_display_name(&min_stat, | |
774 | targ_name, | |
775 | &tname, | |
776 | (gss_OID *) NULL); | |
777 | if (maj_stat != GSS_S_COMPLETE) | |
778 | { | |
779 | LOG_STATUS("displaying target name", maj_stat, min_stat); | |
780 | return LDAP_OPERATIONS_ERROR; | |
781 | } | |
782 | if (debug_) | |
783 | { | |
784 | fprintf(stderr, | |
785 | "\"%.*s\" to \"%.*s\", lifetime %d, flags %x, %s, %s\n", | |
786 | (int) sname.length, (char *) sname.value, | |
787 | (int) tname.length, (char *) tname.value, lifetime, | |
788 | context_flags, | |
789 | (is_local) ? "locally initiated" : "remotely initiated", | |
790 | (is_open) ? "open" : "closed"); | |
791 | fflush(stderr); | |
792 | } | |
793 | (void) gss_release_name(&min_stat, &src_name); | |
794 | (void) gss_release_name(&min_stat, &targ_name); | |
795 | (void) gss_release_buffer(&min_stat, &sname); | |
796 | (void) gss_release_buffer(&min_stat, &tname); | |
5d0a7127 | 797 | #endif |
cd9e6b16 | 798 | state.rc = LDAP_SUCCESS; |
799 | } | |
5d0a7127 | 800 | |
cd9e6b16 | 801 | if (state.rc == LDAP_SUCCESS) |
802 | { | |
b9fa546a | 803 | if (layer == GSSSASL_PRIVACY_PROTECTION) |
cd9e6b16 | 804 | { |
805 | memset(&iofns, 0, sizeof(iofns)); | |
806 | iofns.liof_read = ldap_gssapi_read; | |
807 | iofns.liof_write = ldap_gssapi_write; | |
808 | state.rc = ldap_set_option(ld, LDAP_OPT_IO_FN_PTRS, &iofns); | |
809 | } | |
810 | } | |
811 | ||
812 | NSLDAPI_FREE(service_name); | |
813 | LDAP_SET_LDERRNO(ld, state.rc, NULL, NULL); | |
814 | ||
815 | return state.rc; | |
5d0a7127 | 816 | } |
817 | ||
818 | /* Wrap and encode a security negotiation token */ | |
cd9e6b16 | 819 | unsigned long gsssasl_pack_security_token(OM_uint32 *min_stat, |
820 | gss_ctx_id_t context, | |
821 | gsssasl_security_negotiation_t inmask, | |
822 | size_t masklength, gss_buffer_t wrapped_tok) | |
5d0a7127 | 823 | { |
cd9e6b16 | 824 | OM_uint32 maj_stat; |
825 | OM_uint32 rc; | |
5d0a7127 | 826 | gss_buffer_desc send_tok; |
827 | ||
828 | wrapped_tok->length = 0; | |
829 | wrapped_tok->value = NULL; | |
830 | ||
777bcce2 | 831 | /* |
5d0a7127 | 832 | if (masklength < sizeof(gsssasl_security_negotiation_desc)) |
833 | return GSS_S_FAILURE; | |
777bcce2 | 834 | */ |
5d0a7127 | 835 | |
777bcce2 | 836 | inmask->token_size = inmask->token_size; |
5d0a7127 | 837 | |
838 | send_tok.length = masklength; | |
839 | send_tok.value = inmask; | |
840 | ||
cd9e6b16 | 841 | maj_stat = gss_wrap(min_stat, |
842 | context, | |
843 | 0, | |
844 | GSS_C_QOP_DEFAULT, | |
845 | &send_tok, | |
846 | (int *)&rc, | |
847 | wrapped_tok); | |
5d0a7127 | 848 | |
777bcce2 | 849 | /* inmask->token_size = ntohl(inmask->token_size);*/ |
5d0a7127 | 850 | |
851 | return maj_stat; | |
852 | } | |
853 | ||
854 | /* Unwrap and decode a security negotiation token. */ | |
cd9e6b16 | 855 | int gsssasl_unpack_security_token(OM_uint32 *min_stat, gss_ctx_id_t context, |
856 | gss_buffer_t wrapped_tok, | |
857 | gsssasl_security_negotiation_t *outmask, | |
858 | size_t *masklength) | |
5d0a7127 | 859 | { |
cd9e6b16 | 860 | OM_uint32 maj_stat; |
861 | OM_uint32 rc; | |
5d0a7127 | 862 | gss_buffer_desc recv_tok; |
863 | ||
864 | *masklength = 0; | |
865 | *outmask = NULL; | |
866 | memset(&recv_tok, '\0', sizeof(recv_tok)); | |
867 | ||
cd9e6b16 | 868 | maj_stat = gss_unwrap(min_stat, |
869 | context, | |
870 | wrapped_tok, | |
871 | &recv_tok, | |
872 | (int *)&rc, | |
873 | (gss_qop_t *) NULL); | |
5d0a7127 | 874 | if (maj_stat != GSS_S_COMPLETE) |
875 | return maj_stat; | |
876 | ||
cd9e6b16 | 877 | /* |
5d0a7127 | 878 | #ifdef _WIN32 |
879 | if (recv_tok.length < sizeof(gsssasl_security_negotiation_desc)) | |
880 | { | |
881 | gss_release_buffer(&rc, &recv_tok); | |
882 | return GSS_S_FAILURE; | |
883 | } | |
cd9e6b16 | 884 | #endif |
885 | */ | |
5d0a7127 | 886 | |
cd9e6b16 | 887 | /* |
888 | * we're lazy and don't copy the token. This could cause | |
889 | * problems if libgssapi uses a different malloc! | |
890 | */ | |
5d0a7127 | 891 | *masklength = recv_tok.length; |
892 | *outmask = (gsssasl_security_negotiation_t) recv_tok.value; | |
893 | if (*outmask == NULL) | |
894 | { | |
895 | gss_release_buffer(&rc, &recv_tok); | |
896 | return GSS_S_FAILURE; | |
897 | } | |
898 | return GSS_S_COMPLETE; | |
899 | } | |
900 | ||
901 | int ldap_reset_principal(LDAP *ld, char *service_name, gss_name_t target_name) | |
902 | { | |
cd9e6b16 | 903 | krb5_context context = NULL; |
904 | krb5_principal princ; | |
905 | krb5_principal princ1; | |
906 | krb5_principal temp_princ; | |
907 | char *realm; | |
908 | char *server_name = NULL; | |
909 | int i; | |
5d0a7127 | 910 | |
5d0a7127 | 911 | princ = (krb5_principal)(target_name); |
cd9e6b16 | 912 | |
5d0a7127 | 913 | if (krb5_init_context(&context)) |
cd9e6b16 | 914 | return(-1); |
915 | ||
916 | realm = strdup(ldap_domain_name); | |
917 | for (i = 0; i < (int)strlen(realm); i++) | |
918 | realm[i] = toupper(realm[i]); | |
919 | server_name = strdup(ld->ld_defhost); | |
920 | for (i = 0; i < (int)strlen(server_name); i++) | |
921 | server_name[i] = tolower(server_name[i]); | |
5d0a7127 | 922 | |
5d0a7127 | 923 | temp_princ = malloc(sizeof(*princ)); |
924 | memcpy(temp_princ, princ, sizeof(*princ)); | |
925 | ||
cd9e6b16 | 926 | krb5_build_principal(context, &princ1, strlen(realm), realm, "ldap", |
927 | server_name, ldap_domain_name, 0); | |
5d0a7127 | 928 | |
929 | memcpy(princ, princ1, sizeof(*princ1)); | |
930 | free(princ1); | |
931 | krb5_free_principal(context, temp_princ); | |
cd9e6b16 | 932 | |
933 | if (realm != NULL) | |
934 | free(realm); | |
935 | if (server_name != NULL) | |
936 | free(server_name); | |
5d0a7127 | 937 | if (context != NULL) |
938 | krb5_free_context(context); | |
cd9e6b16 | 939 | return(0); |
5d0a7127 | 940 | } |
941 | ||
942 | int ldap_delete_tickets(LDAP *ld, char *service_name) | |
943 | { | |
cd9e6b16 | 944 | int i; |
945 | int rc; | |
946 | krb5_context context = NULL; | |
947 | krb5_ccache v5Cache = NULL; | |
948 | krb5_creds creds; | |
949 | krb5_cc_cursor v5Cursor; | |
5d0a7127 | 950 | krb5_error_code code; |
cd9e6b16 | 951 | char *sServerName; |
5d0a7127 | 952 | |
953 | if (!NSLDAPI_VALID_LDAP_POINTER(ld) || ld->ld_defhost == NULL) | |
cd9e6b16 | 954 | { |
955 | return -1; | |
956 | } | |
5d0a7127 | 957 | |
958 | for (i = 0; i < (int)strlen(ld->ld_defhost); i++) | |
959 | ld->ld_defhost[i] = toupper(ld->ld_defhost[i]); | |
960 | ||
961 | rc = -1; | |
962 | ||
963 | if (krb5_init_context(&context)) | |
964 | return(rc); | |
965 | ||
966 | if (krb5_cc_default(context, &v5Cache)) | |
967 | goto cleanup; | |
968 | if (krb5_cc_start_seq_get(context, v5Cache, &v5Cursor)) | |
969 | goto cleanup; | |
970 | ||
971 | memset(&creds, '\0', sizeof(creds)); | |
972 | ||
973 | while (!(code = krb5_cc_next_cred(context, v5Cache, &v5Cursor, &creds))) | |
974 | { | |
975 | if (krb5_unparse_name(context, creds.server, &sServerName)) | |
cd9e6b16 | 976 | { |
977 | krb5_free_cred_contents(context, &creds); | |
978 | continue; | |
979 | } | |
5d0a7127 | 980 | if (!memcmp(sServerName, service_name, strlen(service_name))) |
cd9e6b16 | 981 | { |
982 | krb5_cc_remove_cred(context, v5Cache, 0, &creds); | |
983 | } | |
5d0a7127 | 984 | continue; |
985 | } | |
cd9e6b16 | 986 | |
5d0a7127 | 987 | if ((code == KRB5_CC_END) || (code == KRB5_CC_NOTFOUND)) |
cd9e6b16 | 988 | { |
989 | krb5_cc_end_seq_get(context, v5Cache, &v5Cursor); | |
990 | } | |
5d0a7127 | 991 | rc = 0; |
992 | ||
993 | cleanup: | |
994 | if ((v5Cache != NULL) && (context != NULL)) | |
995 | krb5_cc_close(context, v5Cache); | |
996 | if (context != NULL) | |
997 | krb5_free_context(context); | |
998 | return(rc); | |
999 | } | |
cd9e6b16 | 1000 | |
1001 | int locate_ldap_server(char *domain, char **server_name) | |
1002 | { | |
1003 | char service[128]; | |
1004 | char host[128]; | |
1005 | int location_type; | |
1006 | int length; | |
1007 | int rc; | |
1008 | int return_code; | |
1009 | int entry_length; | |
1010 | int server_count; | |
1011 | unsigned char reply[1024]; | |
1012 | unsigned char *ptr; | |
1013 | ||
1014 | strcpy(ldap_domain_name, domain); | |
1015 | sprintf(service, "%s.%s.%s.", LDAP_SERVICE, TCP_PROTOCOL, domain); | |
1016 | ||
1017 | return_code = -1; | |
1018 | server_count = 0; | |
1019 | memset(reply, '\0', sizeof(reply)); | |
1020 | length = res_search(service, C_IN, T_SRV, reply, sizeof(reply)); | |
1021 | if (length >= 0) | |
1022 | { | |
1023 | ptr = reply; | |
1024 | ptr += sizeof(HEADER); | |
1025 | if ((rc = dn_expand(reply, reply + length, ptr, host, | |
1026 | sizeof(host))) < 0) | |
1027 | return(-1); | |
1028 | ptr += (rc + 4); | |
1029 | ||
1030 | while (ptr < reply + length) | |
1031 | { | |
1032 | if ((rc = dn_expand(reply, reply + length, ptr, host, | |
1033 | sizeof(host))) < 0) | |
1034 | break; | |
1035 | ptr += rc; | |
1036 | location_type = (ptr[0] << 8) | ptr[1]; | |
1037 | ptr += 8; | |
1038 | entry_length = (ptr[0] << 8) | ptr[1]; | |
1039 | ptr += 2; | |
1040 | if (location_type == T_SRV) | |
1041 | { | |
1042 | if ((rc = dn_expand(reply, reply + length, ptr + 6, host, | |
1043 | sizeof(host))) < 0) | |
1044 | return -1; | |
1045 | ||
1046 | (*server_name) = strdup(host); | |
1047 | ++server_name; | |
1048 | return_code = 1; | |
1049 | server_count++; | |
1050 | } | |
1051 | ptr += entry_length; | |
1052 | } | |
1053 | } | |
1054 | return(return_code); | |
1055 | } |