source: trunk/server/source3/libads/sasl.c

Last change on this file was 920, checked in by Silvan Scherrer, 9 years ago

Samba Server: apply latest security patches to trunk

File size: 32.9 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 ads sasl code
4 Copyright (C) Andrew Tridgell 2001
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "includes.h"
21#include "../libcli/auth/spnego.h"
22#include "../libcli/auth/ntlmssp.h"
23#include "ads.h"
24#include "smb_krb5.h"
25
26#ifdef HAVE_LDAP
27
28static ADS_STATUS ads_sasl_ntlmssp_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
29{
30 struct ntlmssp_state *ntlmssp_state =
31 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
32 ADS_STATUS status;
33 NTSTATUS nt_status;
34 DATA_BLOB sig;
35 TALLOC_CTX *frame;
36 uint8 *dptr = ads->ldap.out.buf + (4 + NTLMSSP_SIG_SIZE);
37
38 frame = talloc_stackframe();
39 /* copy the data to the right location */
40 memcpy(dptr, buf, len);
41
42 /* create the signature and may encrypt the data */
43 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
44 nt_status = ntlmssp_seal_packet(ntlmssp_state,
45 frame,
46 dptr, len,
47 dptr, len,
48 &sig);
49 } else {
50 nt_status = ntlmssp_sign_packet(ntlmssp_state,
51 frame,
52 dptr, len,
53 dptr, len,
54 &sig);
55 }
56 status = ADS_ERROR_NT(nt_status);
57 if (!ADS_ERR_OK(status)) return status;
58
59 /* copy the signature to the right location */
60 memcpy(ads->ldap.out.buf + 4,
61 sig.data, NTLMSSP_SIG_SIZE);
62
63 TALLOC_FREE(frame);
64
65 /* set how many bytes must be written to the underlying socket */
66 ads->ldap.out.left = 4 + NTLMSSP_SIG_SIZE + len;
67
68 return ADS_SUCCESS;
69}
70
71static ADS_STATUS ads_sasl_ntlmssp_unwrap(ADS_STRUCT *ads)
72{
73 struct ntlmssp_state *ntlmssp_state =
74 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
75 ADS_STATUS status;
76 NTSTATUS nt_status;
77 DATA_BLOB sig;
78 uint8 *dptr = ads->ldap.in.buf + (4 + NTLMSSP_SIG_SIZE);
79 uint32 dlen = ads->ldap.in.ofs - (4 + NTLMSSP_SIG_SIZE);
80
81 /* wrap the signature into a DATA_BLOB */
82 sig = data_blob_const(ads->ldap.in.buf + 4, NTLMSSP_SIG_SIZE);
83
84 /* verify the signature and maybe decrypt the data */
85 if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL) {
86 nt_status = ntlmssp_unseal_packet(ntlmssp_state,
87 dptr, dlen,
88 dptr, dlen,
89 &sig);
90 } else {
91 nt_status = ntlmssp_check_packet(ntlmssp_state,
92 dptr, dlen,
93 dptr, dlen,
94 &sig);
95 }
96 status = ADS_ERROR_NT(nt_status);
97 if (!ADS_ERR_OK(status)) return status;
98
99 /* set the amount of bytes for the upper layer and set the ofs to the data */
100 ads->ldap.in.left = dlen;
101 ads->ldap.in.ofs = 4 + NTLMSSP_SIG_SIZE;
102
103 return ADS_SUCCESS;
104}
105
106static void ads_sasl_ntlmssp_disconnect(ADS_STRUCT *ads)
107{
108 struct ntlmssp_state *ntlmssp_state =
109 (struct ntlmssp_state *)ads->ldap.wrap_private_data;
110
111 TALLOC_FREE(ntlmssp_state);
112
113 ads->ldap.wrap_ops = NULL;
114 ads->ldap.wrap_private_data = NULL;
115}
116
117static const struct ads_saslwrap_ops ads_sasl_ntlmssp_ops = {
118 .name = "ntlmssp",
119 .wrap = ads_sasl_ntlmssp_wrap,
120 .unwrap = ads_sasl_ntlmssp_unwrap,
121 .disconnect = ads_sasl_ntlmssp_disconnect
122};
123
124/*
125 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
126 we fit on one socket??)
127*/
128static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
129{
130 DATA_BLOB msg1 = data_blob_null;
131 DATA_BLOB blob = data_blob_null;
132 DATA_BLOB blob_in = data_blob_null;
133 DATA_BLOB blob_out = data_blob_null;
134 struct berval cred, *scred = NULL;
135 int rc;
136 NTSTATUS nt_status;
137 ADS_STATUS status;
138 int turn = 1;
139 uint32 features = 0;
140
141 struct ntlmssp_state *ntlmssp_state;
142
143 nt_status = ntlmssp_client_start(NULL,
144 global_myname(),
145 lp_workgroup(),
146 lp_client_ntlmv2_auth(),
147 &ntlmssp_state);
148 if (!NT_STATUS_IS_OK(nt_status)) {
149 return ADS_ERROR_NT(nt_status);
150 }
151 ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
152
153 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
154 return ADS_ERROR_NT(nt_status);
155 }
156 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
157 return ADS_ERROR_NT(nt_status);
158 }
159 if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
160 return ADS_ERROR_NT(nt_status);
161 }
162
163 switch (ads->ldap.wrap_type) {
164 case ADS_SASLWRAP_TYPE_SEAL:
165 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
166 break;
167 case ADS_SASLWRAP_TYPE_SIGN:
168 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
169 features = NTLMSSP_FEATURE_SIGN;
170 } else {
171 /*
172 * windows servers are broken with sign only,
173 * so we need to use seal here too
174 */
175 features = NTLMSSP_FEATURE_SIGN | NTLMSSP_FEATURE_SEAL;
176 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
177 }
178 break;
179 case ADS_SASLWRAP_TYPE_PLAIN:
180 break;
181 }
182
183 ntlmssp_want_feature(ntlmssp_state, features);
184
185 blob_in = data_blob_null;
186
187 do {
188 nt_status = ntlmssp_update(ntlmssp_state,
189 blob_in, &blob_out);
190 data_blob_free(&blob_in);
191 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)
192 || NT_STATUS_IS_OK(nt_status))
193 && blob_out.length) {
194 if (turn == 1) {
195 const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL};
196 /* and wrap it in a SPNEGO wrapper */
197 msg1 = spnego_gen_negTokenInit(talloc_tos(),
198 OIDs_ntlm, &blob_out, NULL);
199 } else {
200 /* wrap it in SPNEGO */
201 msg1 = spnego_gen_auth(talloc_tos(), blob_out);
202 }
203
204 data_blob_free(&blob_out);
205
206 cred.bv_val = (char *)msg1.data;
207 cred.bv_len = msg1.length;
208 scred = NULL;
209 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
210 data_blob_free(&msg1);
211 if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
212 if (scred) {
213 ber_bvfree(scred);
214 }
215
216 TALLOC_FREE(ntlmssp_state);
217 return ADS_ERROR(rc);
218 }
219 if (scred) {
220 blob = data_blob(scred->bv_val, scred->bv_len);
221 ber_bvfree(scred);
222 } else {
223 blob = data_blob_null;
224 }
225
226 } else {
227
228 TALLOC_FREE(ntlmssp_state);
229 data_blob_free(&blob_out);
230 return ADS_ERROR_NT(nt_status);
231 }
232
233 if ((turn == 1) &&
234 (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
235 DATA_BLOB tmp_blob = data_blob_null;
236 /* the server might give us back two challenges */
237 if (!spnego_parse_challenge(talloc_tos(), blob, &blob_in,
238 &tmp_blob)) {
239
240 TALLOC_FREE(ntlmssp_state);
241 data_blob_free(&blob);
242 DEBUG(3,("Failed to parse challenges\n"));
243 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
244 }
245 data_blob_free(&tmp_blob);
246 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
247 if (!spnego_parse_auth_response(talloc_tos(), blob, nt_status, OID_NTLMSSP,
248 &blob_in)) {
249
250 TALLOC_FREE(ntlmssp_state);
251 data_blob_free(&blob);
252 DEBUG(3,("Failed to parse auth response\n"));
253 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
254 }
255 }
256 data_blob_free(&blob);
257 data_blob_free(&blob_out);
258 turn++;
259 } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
260
261 /* we have a reference conter on ntlmssp_state, if we are signing
262 then the state will be kept by the signing engine */
263
264 if (ads->ldap.wrap_type >= ADS_SASLWRAP_TYPE_SEAL) {
265 bool ok;
266
267 ok = ntlmssp_have_feature(ntlmssp_state,
268 NTLMSSP_FEATURE_SEAL);
269 if (!ok) {
270 DEBUG(0,("The ntlmssp feature sealing request, but unavailable\n"));
271 TALLOC_FREE(ntlmssp_state);
272 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
273 }
274
275 ok = ntlmssp_have_feature(ntlmssp_state,
276 NTLMSSP_FEATURE_SIGN);
277 if (!ok) {
278 DEBUG(0,("The ntlmssp feature signing request, but unavailable\n"));
279 TALLOC_FREE(ntlmssp_state);
280 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
281 }
282
283 } else if (ads->ldap.wrap_type >= ADS_SASLWRAP_TYPE_SIGN) {
284 bool ok;
285
286 ok = ntlmssp_have_feature(ntlmssp_state,
287 NTLMSSP_FEATURE_SIGN);
288 if (!ok) {
289 DEBUG(0,("The gensec feature signing request, but unavailable\n"));
290 TALLOC_FREE(ntlmssp_state);
291 return ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
292 }
293 }
294
295 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
296 ads->ldap.out.max_unwrapped = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED - NTLMSSP_SIG_SIZE;
297 ads->ldap.out.sig_size = NTLMSSP_SIG_SIZE;
298 ads->ldap.in.min_wrapped = ads->ldap.out.sig_size;
299 ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
300 status = ads_setup_sasl_wrapping(ads, &ads_sasl_ntlmssp_ops, ntlmssp_state);
301 if (!ADS_ERR_OK(status)) {
302 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
303 ads_errstr(status)));
304 TALLOC_FREE(ntlmssp_state);
305 return status;
306 }
307 } else {
308 TALLOC_FREE(ntlmssp_state);
309 }
310
311 return ADS_ERROR(rc);
312}
313
314#ifdef HAVE_GSSAPI
315static ADS_STATUS ads_sasl_gssapi_wrap(ADS_STRUCT *ads, uint8 *buf, uint32 len)
316{
317 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
318 ADS_STATUS status;
319 int gss_rc;
320 uint32 minor_status;
321 gss_buffer_desc unwrapped, wrapped;
322 int conf_req_flag, conf_state;
323
324 unwrapped.value = buf;
325 unwrapped.length = len;
326
327 /* for now request sign and seal */
328 conf_req_flag = (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL);
329
330 gss_rc = gss_wrap(&minor_status, context_handle,
331 conf_req_flag, GSS_C_QOP_DEFAULT,
332 &unwrapped, &conf_state,
333 &wrapped);
334 status = ADS_ERROR_GSS(gss_rc, minor_status);
335 if (!ADS_ERR_OK(status)) return status;
336
337 if (conf_req_flag && conf_state == 0) {
338 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
339 }
340
341 if ((ads->ldap.out.size - 4) < wrapped.length) {
342 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
343 }
344
345 /* copy the wrapped blob to the right location */
346 memcpy(ads->ldap.out.buf + 4, wrapped.value, wrapped.length);
347
348 /* set how many bytes must be written to the underlying socket */
349 ads->ldap.out.left = 4 + wrapped.length;
350
351 gss_release_buffer(&minor_status, &wrapped);
352
353 return ADS_SUCCESS;
354}
355
356static ADS_STATUS ads_sasl_gssapi_unwrap(ADS_STRUCT *ads)
357{
358 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
359 ADS_STATUS status;
360 int gss_rc;
361 uint32 minor_status;
362 gss_buffer_desc unwrapped, wrapped;
363 int conf_state;
364
365 wrapped.value = ads->ldap.in.buf + 4;
366 wrapped.length = ads->ldap.in.ofs - 4;
367
368 gss_rc = gss_unwrap(&minor_status, context_handle,
369 &wrapped, &unwrapped,
370 &conf_state, GSS_C_QOP_DEFAULT);
371 status = ADS_ERROR_GSS(gss_rc, minor_status);
372 if (!ADS_ERR_OK(status)) return status;
373
374 if (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL && conf_state == 0) {
375 return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
376 }
377
378 if (wrapped.length < unwrapped.length) {
379 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
380 }
381
382 /* copy the wrapped blob to the right location */
383 memcpy(ads->ldap.in.buf + 4, unwrapped.value, unwrapped.length);
384
385 /* set how many bytes must be written to the underlying socket */
386 ads->ldap.in.left = unwrapped.length;
387 ads->ldap.in.ofs = 4;
388
389 gss_release_buffer(&minor_status, &unwrapped);
390
391 return ADS_SUCCESS;
392}
393
394static void ads_sasl_gssapi_disconnect(ADS_STRUCT *ads)
395{
396 gss_ctx_id_t context_handle = (gss_ctx_id_t)ads->ldap.wrap_private_data;
397 uint32 minor_status;
398
399 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
400
401 ads->ldap.wrap_ops = NULL;
402 ads->ldap.wrap_private_data = NULL;
403}
404
405static const struct ads_saslwrap_ops ads_sasl_gssapi_ops = {
406 .name = "gssapi",
407 .wrap = ads_sasl_gssapi_wrap,
408 .unwrap = ads_sasl_gssapi_unwrap,
409 .disconnect = ads_sasl_gssapi_disconnect
410};
411
412/*
413 perform a LDAP/SASL/SPNEGO/GSSKRB5 bind
414*/
415static ADS_STATUS ads_sasl_spnego_gsskrb5_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
416{
417 ADS_STATUS status;
418 bool ok;
419 uint32 minor_status;
420 int gss_rc, rc;
421 gss_OID_desc krb5_mech_type =
422 {9, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") };
423 gss_OID mech_type = &krb5_mech_type;
424 gss_OID actual_mech_type = GSS_C_NULL_OID;
425 const char *spnego_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
426 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
427 gss_buffer_desc input_token, output_token;
428 uint32 req_flags, ret_flags;
429 uint32 req_tmp, ret_tmp;
430 DATA_BLOB unwrapped;
431 DATA_BLOB wrapped;
432 struct berval cred, *scred = NULL;
433
434 input_token.value = NULL;
435 input_token.length = 0;
436
437 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
438 switch (ads->ldap.wrap_type) {
439 case ADS_SASLWRAP_TYPE_SEAL:
440 req_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
441 break;
442 case ADS_SASLWRAP_TYPE_SIGN:
443 req_flags |= GSS_C_INTEG_FLAG;
444 break;
445 case ADS_SASLWRAP_TYPE_PLAIN:
446 break;
447 }
448
449 /* Note: here we explicit ask for the krb5 mech_type */
450 gss_rc = gss_init_sec_context(&minor_status,
451 GSS_C_NO_CREDENTIAL,
452 &context_handle,
453 serv_name,
454 mech_type,
455 req_flags,
456 0,
457 NULL,
458 &input_token,
459 &actual_mech_type,
460 &output_token,
461 &ret_flags,
462 NULL);
463 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
464 status = ADS_ERROR_GSS(gss_rc, minor_status);
465 goto failed;
466 }
467
468 /*
469 * As some gssapi krb5 mech implementations
470 * automaticly add GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG
471 * to req_flags internaly, it's not possible to
472 * use plain or signing only connection via
473 * the gssapi interface.
474 *
475 * Because of this we need to check it the ret_flags
476 * has more flags as req_flags and correct the value
477 * of ads->ldap.wrap_type.
478 *
479 * I ads->auth.flags has ADS_AUTH_SASL_FORCE
480 * we need to give an error.
481 */
482 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
483 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
484
485 if (req_tmp == ret_tmp) {
486 /* everythings fine... */
487
488 } else if (req_flags & GSS_C_CONF_FLAG) {
489 /*
490 * here we wanted sealing but didn't got it
491 * from the gssapi library
492 */
493 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
494 goto failed;
495
496 } else if ((req_flags & GSS_C_INTEG_FLAG) &&
497 !(ret_flags & GSS_C_INTEG_FLAG)) {
498 /*
499 * here we wanted siging but didn't got it
500 * from the gssapi library
501 */
502 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
503 goto failed;
504
505 } else if (ret_flags & GSS_C_CONF_FLAG) {
506 /*
507 * here we didn't want sealing
508 * but the gssapi library forces it
509 * so correct the needed wrap_type if
510 * the caller didn't forced siging only
511 */
512 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
513 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
514 goto failed;
515 }
516
517 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
518 req_flags = ret_flags;
519
520 } else if (ret_flags & GSS_C_INTEG_FLAG) {
521 /*
522 * here we didn't want signing
523 * but the gssapi library forces it
524 * so correct the needed wrap_type if
525 * the caller didn't forced plain
526 */
527 if (ads->auth.flags & ADS_AUTH_SASL_FORCE) {
528 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
529 goto failed;
530 }
531
532 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
533 req_flags = ret_flags;
534 } else {
535 /*
536 * This could (should?) not happen
537 */
538 status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
539 goto failed;
540
541 }
542
543 /* and wrap that in a shiny SPNEGO wrapper */
544 unwrapped = data_blob_const(output_token.value, output_token.length);
545 wrapped = spnego_gen_negTokenInit(talloc_tos(),
546 spnego_mechs, &unwrapped, NULL);
547 gss_release_buffer(&minor_status, &output_token);
548 if (unwrapped.length > wrapped.length) {
549 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
550 goto failed;
551 }
552
553 cred.bv_val = (char *)wrapped.data;
554 cred.bv_len = wrapped.length;
555
556 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL,
557 &scred);
558 data_blob_free(&wrapped);
559 if (rc != LDAP_SUCCESS) {
560 status = ADS_ERROR(rc);
561 goto failed;
562 }
563
564 if (scred) {
565 wrapped = data_blob_const(scred->bv_val, scred->bv_len);
566 } else {
567 wrapped = data_blob_null;
568 }
569
570 ok = spnego_parse_auth_response(talloc_tos(), wrapped, NT_STATUS_OK,
571 OID_KERBEROS5_OLD,
572 &unwrapped);
573 if (scred) ber_bvfree(scred);
574 if (!ok) {
575 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
576 goto failed;
577 }
578
579 input_token.value = unwrapped.data;
580 input_token.length = unwrapped.length;
581
582 /*
583 * As we asked for mutal authentication
584 * we need to pass the servers response
585 * to gssapi
586 */
587 gss_rc = gss_init_sec_context(&minor_status,
588 GSS_C_NO_CREDENTIAL,
589 &context_handle,
590 serv_name,
591 mech_type,
592 req_flags,
593 0,
594 NULL,
595 &input_token,
596 &actual_mech_type,
597 &output_token,
598 &ret_flags,
599 NULL);
600 data_blob_free(&unwrapped);
601 if (gss_rc) {
602 status = ADS_ERROR_GSS(gss_rc, minor_status);
603 goto failed;
604 }
605
606 gss_release_buffer(&minor_status, &output_token);
607
608 /*
609 * If we the sign and seal options
610 * doesn't match after getting the response
611 * from the server, we don't want to use the connection
612 */
613 req_tmp = req_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
614 ret_tmp = ret_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
615
616 if (req_tmp != ret_tmp) {
617 /* everythings fine... */
618 status = ADS_ERROR_NT(NT_STATUS_INVALID_NETWORK_RESPONSE);
619 goto failed;
620 }
621
622 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
623 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
624
625 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
626 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
627 GSS_C_QOP_DEFAULT,
628 max_msg_size, &ads->ldap.out.max_unwrapped);
629 if (gss_rc) {
630 status = ADS_ERROR_GSS(gss_rc, minor_status);
631 goto failed;
632 }
633
634 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
635 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
636 ads->ldap.in.max_wrapped = max_msg_size;
637 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
638 if (!ADS_ERR_OK(status)) {
639 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
640 ads_errstr(status)));
641 goto failed;
642 }
643 /* make sure we don't free context_handle */
644 context_handle = GSS_C_NO_CONTEXT;
645 }
646
647 status = ADS_SUCCESS;
648
649failed:
650 if (context_handle != GSS_C_NO_CONTEXT)
651 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
652 return status;
653}
654
655#endif /* HAVE_GSSAPI */
656
657#ifdef HAVE_KRB5
658struct ads_service_principal {
659 char *string;
660#ifdef HAVE_GSSAPI
661 gss_name_t name;
662#endif
663};
664
665static void ads_free_service_principal(struct ads_service_principal *p)
666{
667 SAFE_FREE(p->string);
668
669#ifdef HAVE_GSSAPI
670 if (p->name) {
671 uint32 minor_status;
672 gss_release_name(&minor_status, &p->name);
673 }
674#endif
675 ZERO_STRUCTP(p);
676}
677
678
679static ADS_STATUS ads_guess_service_principal(ADS_STRUCT *ads,
680 char **returned_principal)
681{
682 char *princ = NULL;
683
684 if (ads->server.realm && ads->server.ldap_server) {
685 char *server, *server_realm;
686
687 server = SMB_STRDUP(ads->server.ldap_server);
688 server_realm = SMB_STRDUP(ads->server.realm);
689
690 if (!server || !server_realm) {
691 SAFE_FREE(server);
692 SAFE_FREE(server_realm);
693 return ADS_ERROR(LDAP_NO_MEMORY);
694 }
695
696 strlower_m(server);
697 strupper_m(server_realm);
698 if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
699 SAFE_FREE(server);
700 SAFE_FREE(server_realm);
701 return ADS_ERROR(LDAP_NO_MEMORY);
702 }
703
704 SAFE_FREE(server);
705 SAFE_FREE(server_realm);
706
707 if (!princ) {
708 return ADS_ERROR(LDAP_NO_MEMORY);
709 }
710 } else if (ads->config.realm && ads->config.ldap_server_name) {
711 char *server, *server_realm;
712
713 server = SMB_STRDUP(ads->config.ldap_server_name);
714 server_realm = SMB_STRDUP(ads->config.realm);
715
716 if (!server || !server_realm) {
717 SAFE_FREE(server);
718 SAFE_FREE(server_realm);
719 return ADS_ERROR(LDAP_NO_MEMORY);
720 }
721
722 strlower_m(server);
723 strupper_m(server_realm);
724 if (asprintf(&princ, "ldap/%s@%s", server, server_realm) == -1) {
725 SAFE_FREE(server);
726 SAFE_FREE(server_realm);
727 return ADS_ERROR(LDAP_NO_MEMORY);
728 }
729
730 SAFE_FREE(server);
731 SAFE_FREE(server_realm);
732
733 if (!princ) {
734 return ADS_ERROR(LDAP_NO_MEMORY);
735 }
736 }
737
738 if (!princ) {
739 return ADS_ERROR(LDAP_PARAM_ERROR);
740 }
741
742 *returned_principal = princ;
743
744 return ADS_SUCCESS;
745}
746
747static ADS_STATUS ads_generate_service_principal(ADS_STRUCT *ads,
748 const char *given_principal,
749 struct ads_service_principal *p)
750{
751 ADS_STATUS status;
752#ifdef HAVE_GSSAPI
753 gss_buffer_desc input_name;
754 /* GSS_KRB5_NT_PRINCIPAL_NAME */
755 gss_OID_desc nt_principal =
756 {10, CONST_DISCARD(char *, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01")};
757 uint32 minor_status;
758 int gss_rc;
759#endif
760
761 ZERO_STRUCTP(p);
762
763 /* I've seen a child Windows 2000 domain not send
764 the principal name back in the first round of
765 the SASL bind reply. So we guess based on server
766 name and realm. --jerry */
767 /* Also try best guess when we get the w2k8 ignore principal
768 back, or when we are configured to ignore it - gd,
769 abartlet */
770
771 if (!lp_client_use_spnego_principal() ||
772 !given_principal ||
773 strequal(given_principal, ADS_IGNORE_PRINCIPAL)) {
774
775 status = ads_guess_service_principal(ads, &p->string);
776 if (!ADS_ERR_OK(status)) {
777 return status;
778 }
779 } else {
780 p->string = SMB_STRDUP(given_principal);
781 if (!p->string) {
782 return ADS_ERROR(LDAP_NO_MEMORY);
783 }
784 }
785
786#ifdef HAVE_GSSAPI
787 input_name.value = p->string;
788 input_name.length = strlen(p->string);
789
790 gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &p->name);
791 if (gss_rc) {
792 ads_free_service_principal(p);
793 return ADS_ERROR_GSS(gss_rc, minor_status);
794 }
795#endif
796
797 return ADS_SUCCESS;
798}
799
800/*
801 perform a LDAP/SASL/SPNEGO/KRB5 bind
802*/
803static ADS_STATUS ads_sasl_spnego_rawkrb5_bind(ADS_STRUCT *ads, const char *principal)
804{
805 DATA_BLOB blob = data_blob_null;
806 struct berval cred, *scred = NULL;
807 DATA_BLOB session_key = data_blob_null;
808 int rc;
809
810 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
811 return ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
812 }
813
814 rc = spnego_gen_krb5_negTokenInit(talloc_tos(), principal,
815 ads->auth.time_offset, &blob, &session_key, 0,
816 &ads->auth.tgs_expire);
817
818 if (rc) {
819 return ADS_ERROR_KRB5(rc);
820 }
821
822 /* now send the auth packet and we should be done */
823 cred.bv_val = (char *)blob.data;
824 cred.bv_len = blob.length;
825
826 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
827
828 data_blob_free(&blob);
829 data_blob_free(&session_key);
830 if(scred)
831 ber_bvfree(scred);
832
833 return ADS_ERROR(rc);
834}
835
836static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads,
837 struct ads_service_principal *p)
838{
839#ifdef HAVE_GSSAPI
840 /*
841 * we only use the gsskrb5 based implementation
842 * when sasl sign or seal is requested.
843 *
844 * This has the following reasons:
845 * - it's likely that the gssapi krb5 mech implementation
846 * doesn't support to negotiate plain connections
847 * - the ads_sasl_spnego_rawkrb5_bind is more robust
848 * against clock skew errors
849 */
850 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
851 return ads_sasl_spnego_gsskrb5_bind(ads, p->name);
852 }
853#endif
854 return ads_sasl_spnego_rawkrb5_bind(ads, p->string);
855}
856#endif /* HAVE_KRB5 */
857
858/*
859 this performs a SASL/SPNEGO bind
860*/
861static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
862{
863 struct berval *scred=NULL;
864 int rc, i;
865 ADS_STATUS status;
866 DATA_BLOB blob;
867 char *given_principal = NULL;
868 char *OIDs[ASN1_MAX_OIDS];
869#ifdef HAVE_KRB5
870 bool got_kerberos_mechanism = False;
871#endif
872
873 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
874
875 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
876 status = ADS_ERROR(rc);
877 goto failed;
878 }
879
880 blob = data_blob(scred->bv_val, scred->bv_len);
881
882 ber_bvfree(scred);
883
884#if 0
885 file_save("sasl_spnego.dat", blob.data, blob.length);
886#endif
887
888 /* the server sent us the first part of the SPNEGO exchange in the negprot
889 reply */
890 if (!spnego_parse_negTokenInit(talloc_tos(), blob, OIDs, &given_principal, NULL) ||
891 OIDs[0] == NULL) {
892 data_blob_free(&blob);
893 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
894 goto failed;
895 }
896 data_blob_free(&blob);
897
898 /* make sure the server understands kerberos */
899 for (i=0;OIDs[i];i++) {
900 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
901#ifdef HAVE_KRB5
902 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
903 strcmp(OIDs[i], OID_KERBEROS5) == 0) {
904 got_kerberos_mechanism = True;
905 }
906#endif
907 talloc_free(OIDs[i]);
908 }
909 DEBUG(3,("ads_sasl_spnego_bind: got server principal name = %s\n", given_principal));
910
911#ifdef HAVE_KRB5
912 if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
913 got_kerberos_mechanism)
914 {
915 struct ads_service_principal p;
916
917 status = ads_generate_service_principal(ads, given_principal, &p);
918 TALLOC_FREE(given_principal);
919 if (!ADS_ERR_OK(status)) {
920 return status;
921 }
922
923 status = ads_sasl_spnego_krb5_bind(ads, &p);
924 if (ADS_ERR_OK(status)) {
925 ads_free_service_principal(&p);
926 return status;
927 }
928
929 DEBUG(10,("ads_sasl_spnego_krb5_bind failed with: %s, "
930 "calling kinit\n", ads_errstr(status)));
931
932 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
933
934 if (ADS_ERR_OK(status)) {
935 status = ads_sasl_spnego_krb5_bind(ads, &p);
936 if (!ADS_ERR_OK(status)) {
937 DEBUG(0,("kinit succeeded but "
938 "ads_sasl_spnego_krb5_bind failed: %s\n",
939 ads_errstr(status)));
940 }
941 }
942
943 ads_free_service_principal(&p);
944
945 /* only fallback to NTLMSSP if allowed */
946 if (ADS_ERR_OK(status) ||
947 !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
948 return status;
949 }
950 } else
951#endif
952 {
953 TALLOC_FREE(given_principal);
954 }
955
956 /* lets do NTLMSSP ... this has the big advantage that we don't need
957 to sync clocks, and we don't rely on special versions of the krb5
958 library for HMAC_MD4 encryption */
959 return ads_sasl_spnego_ntlmssp_bind(ads);
960
961failed:
962 return status;
963}
964
965#ifdef HAVE_GSSAPI
966#define MAX_GSS_PASSES 3
967
968/* this performs a SASL/gssapi bind
969 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
970 is very dependent on correctly configured DNS whereas
971 this routine is much less fragile
972 see RFC2078 and RFC2222 for details
973*/
974static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
975{
976 uint32 minor_status;
977 gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
978 gss_OID mech_type = GSS_C_NULL_OID;
979 gss_buffer_desc output_token, input_token;
980 uint32 req_flags, ret_flags;
981 int conf_state;
982 struct berval cred;
983 struct berval *scred = NULL;
984 int i=0;
985 int gss_rc, rc;
986 uint8 *p;
987 uint32 max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
988 uint8 wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
989 ADS_STATUS status;
990
991 input_token.value = NULL;
992 input_token.length = 0;
993
994 /*
995 * Note: here we always ask the gssapi for sign and seal
996 * as this is negotiated later after the mutal
997 * authentication
998 */
999 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
1000
1001 for (i=0; i < MAX_GSS_PASSES; i++) {
1002 gss_rc = gss_init_sec_context(&minor_status,
1003 GSS_C_NO_CREDENTIAL,
1004 &context_handle,
1005 serv_name,
1006 mech_type,
1007 req_flags,
1008 0,
1009 NULL,
1010 &input_token,
1011 NULL,
1012 &output_token,
1013 &ret_flags,
1014 NULL);
1015 if (scred) {
1016 ber_bvfree(scred);
1017 scred = NULL;
1018 }
1019 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
1020 status = ADS_ERROR_GSS(gss_rc, minor_status);
1021 goto failed;
1022 }
1023
1024 cred.bv_val = (char *)output_token.value;
1025 cred.bv_len = output_token.length;
1026
1027 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1028 &scred);
1029 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
1030 status = ADS_ERROR(rc);
1031 goto failed;
1032 }
1033
1034 if (output_token.value) {
1035 gss_release_buffer(&minor_status, &output_token);
1036 }
1037
1038 if (scred) {
1039 input_token.value = scred->bv_val;
1040 input_token.length = scred->bv_len;
1041 } else {
1042 input_token.value = NULL;
1043 input_token.length = 0;
1044 }
1045
1046 if (gss_rc == 0) break;
1047 }
1048
1049 gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
1050 &conf_state,NULL);
1051 if (scred) {
1052 ber_bvfree(scred);
1053 scred = NULL;
1054 }
1055 if (gss_rc) {
1056 status = ADS_ERROR_GSS(gss_rc, minor_status);
1057 goto failed;
1058 }
1059
1060 p = (uint8 *)output_token.value;
1061
1062#if 0
1063 file_save("sasl_gssapi.dat", output_token.value, output_token.length);
1064#endif
1065
1066 if (p) {
1067 wrap_type = CVAL(p,0);
1068 SCVAL(p,0,0);
1069 max_msg_size = RIVAL(p,0);
1070 }
1071
1072 gss_release_buffer(&minor_status, &output_token);
1073
1074 if (!(wrap_type & ads->ldap.wrap_type)) {
1075 /*
1076 * the server doesn't supports the wrap
1077 * type we want :-(
1078 */
1079 DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
1080 ads->ldap.wrap_type, wrap_type));
1081 DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
1082 status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
1083 goto failed;
1084 }
1085
1086 /* 0x58 is the minimum windows accepts */
1087 if (max_msg_size < 0x58) {
1088 max_msg_size = 0x58;
1089 }
1090
1091 output_token.length = 4;
1092 output_token.value = SMB_MALLOC(output_token.length);
1093 if (!output_token.value) {
1094 output_token.length = 0;
1095 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1096 goto failed;
1097 }
1098 p = (uint8 *)output_token.value;
1099
1100 RSIVAL(p,0,max_msg_size);
1101 SCVAL(p,0,ads->ldap.wrap_type);
1102
1103 /*
1104 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
1105 * but using ads->config.bind_path is the wrong! It should be
1106 * the DN of the user object!
1107 *
1108 * w2k3 gives an error when we send an incorrect DN, but sending nothing
1109 * is ok and matches the information flow used in GSS-SPNEGO.
1110 */
1111
1112 gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
1113 &output_token, /* used as *input* here. */
1114 &conf_state,
1115 &input_token); /* Used as *output* here. */
1116 if (gss_rc) {
1117 status = ADS_ERROR_GSS(gss_rc, minor_status);
1118 output_token.length = 0;
1119 SAFE_FREE(output_token.value);
1120 goto failed;
1121 }
1122
1123 /* We've finished with output_token. */
1124 SAFE_FREE(output_token.value);
1125 output_token.length = 0;
1126
1127 cred.bv_val = (char *)input_token.value;
1128 cred.bv_len = input_token.length;
1129
1130 rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL,
1131 &scred);
1132 gss_release_buffer(&minor_status, &input_token);
1133 status = ADS_ERROR(rc);
1134 if (!ADS_ERR_OK(status)) {
1135 goto failed;
1136 }
1137
1138 if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
1139 gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
1140 (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
1141 GSS_C_QOP_DEFAULT,
1142 max_msg_size, &ads->ldap.out.max_unwrapped);
1143 if (gss_rc) {
1144 status = ADS_ERROR_GSS(gss_rc, minor_status);
1145 goto failed;
1146 }
1147
1148 ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
1149 ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
1150 ads->ldap.in.max_wrapped = max_msg_size;
1151 status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
1152 if (!ADS_ERR_OK(status)) {
1153 DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
1154 ads_errstr(status)));
1155 goto failed;
1156 }
1157 /* make sure we don't free context_handle */
1158 context_handle = GSS_C_NO_CONTEXT;
1159 }
1160
1161failed:
1162
1163 if (context_handle != GSS_C_NO_CONTEXT)
1164 gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
1165
1166 if(scred)
1167 ber_bvfree(scred);
1168 return status;
1169}
1170
1171static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
1172{
1173 ADS_STATUS status;
1174 struct ads_service_principal p;
1175
1176 status = ads_generate_service_principal(ads, NULL, &p);
1177 if (!ADS_ERR_OK(status)) {
1178 return status;
1179 }
1180
1181 status = ads_sasl_gssapi_do_bind(ads, p.name);
1182 if (ADS_ERR_OK(status)) {
1183 ads_free_service_principal(&p);
1184 return status;
1185 }
1186
1187 DEBUG(10,("ads_sasl_gssapi_do_bind failed with: %s, "
1188 "calling kinit\n", ads_errstr(status)));
1189
1190 status = ADS_ERROR_KRB5(ads_kinit_password(ads));
1191
1192 if (ADS_ERR_OK(status)) {
1193 status = ads_sasl_gssapi_do_bind(ads, p.name);
1194 }
1195
1196 ads_free_service_principal(&p);
1197
1198 return status;
1199}
1200
1201#endif /* HAVE_GSSAPI */
1202
1203/* mapping between SASL mechanisms and functions */
1204static struct {
1205 const char *name;
1206 ADS_STATUS (*fn)(ADS_STRUCT *);
1207} sasl_mechanisms[] = {
1208 {"GSS-SPNEGO", ads_sasl_spnego_bind},
1209#ifdef HAVE_GSSAPI
1210 {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
1211#endif
1212 {NULL, NULL}
1213};
1214
1215ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
1216{
1217 const char *attrs[] = {"supportedSASLMechanisms", NULL};
1218 char **values;
1219 ADS_STATUS status;
1220 int i, j;
1221 LDAPMessage *res;
1222
1223 /* get a list of supported SASL mechanisms */
1224 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
1225 if (!ADS_ERR_OK(status)) return status;
1226
1227 values = ldap_get_values(ads->ldap.ld, res, "supportedSASLMechanisms");
1228
1229 if (ads->auth.flags & ADS_AUTH_SASL_SEAL) {
1230 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SEAL;
1231 } else if (ads->auth.flags & ADS_AUTH_SASL_SIGN) {
1232 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1233 } else {
1234 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
1235 }
1236
1237 /* try our supported mechanisms in order */
1238 for (i=0;sasl_mechanisms[i].name;i++) {
1239 /* see if the server supports it */
1240 for (j=0;values && values[j];j++) {
1241 if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
1242 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
1243retry:
1244 status = sasl_mechanisms[i].fn(ads);
1245 if (status.error_type == ENUM_ADS_ERROR_LDAP &&
1246 status.err.rc == LDAP_STRONG_AUTH_REQUIRED &&
1247 ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_PLAIN)
1248 {
1249 DEBUG(3,("SASL bin got LDAP_STRONG_AUTH_REQUIRED "
1250 "retrying with signing enabled\n"));
1251 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_SIGN;
1252 goto retry;
1253 }
1254 ldap_value_free(values);
1255 ldap_msgfree(res);
1256 return status;
1257 }
1258 }
1259 }
1260
1261 ldap_value_free(values);
1262 ldap_msgfree(res);
1263 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
1264}
1265
1266#endif /* HAVE_LDAP */
1267
Note: See TracBrowser for help on using the repository browser.