2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2001
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 2 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27 we fit on one socket??)
29 static ADS_STATUS
ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT
*ads
)
31 const char *mechs
[] = {OID_NTLMSSP
, NULL
};
32 DATA_BLOB msg1
= data_blob(NULL
, 0);
33 DATA_BLOB blob
, chal1
, chal2
, auth
;
35 uint8 nthash
[24], lmhash
[24], sess_key
[16];
37 struct berval cred
, *scred
= NULL
;
41 if (!ads
->auth
.password
) {
42 /* No password, don't segfault below... */
43 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE
);
46 neg_flags
= NTLMSSP_NEGOTIATE_UNICODE
|
47 NTLMSSP_NEGOTIATE_128
|
48 NTLMSSP_NEGOTIATE_NTLM
;
50 memset(sess_key
, 0, 16);
52 /* generate the ntlmssp negotiate packet */
53 msrpc_gen(&blob
, "CddB",
59 /* and wrap it in a SPNEGO wrapper */
60 msg1
= gen_negTokenTarg(mechs
, blob
);
61 data_blob_free(&blob
);
63 cred
.bv_val
= (char *)msg1
.data
;
64 cred
.bv_len
= msg1
.length
;
66 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
67 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
68 status
= ADS_ERROR(rc
);
72 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
75 /* the server gives us back two challenges */
76 if (!spnego_parse_challenge(blob
, &chal1
, &chal2
)) {
77 DEBUG(3,("Failed to parse challenges\n"));
78 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
82 data_blob_free(&blob
);
84 /* encrypt the password with the challenge */
85 memcpy(challenge
, chal1
.data
+ 24, 8);
86 SMBencrypt(ads
->auth
.password
, challenge
,lmhash
);
87 SMBNTencrypt(ads
->auth
.password
, challenge
,nthash
);
89 data_blob_free(&chal1
);
90 data_blob_free(&chal2
);
92 /* this generates the actual auth packet */
93 msrpc_gen(&blob
, "CdBBUUUBd",
104 /* wrap it in SPNEGO */
105 auth
= spnego_gen_auth(blob
);
107 data_blob_free(&blob
);
109 /* Remember to free the msg1 blob. The contents of this
110 have been copied into cred and need freeing before reassignment. */
111 data_blob_free(&msg1
);
113 /* now send the auth packet and we should be done */
114 cred
.bv_val
= (char *)auth
.data
;
115 cred
.bv_len
= auth
.length
;
117 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
120 data_blob_free(&auth
);
122 return ADS_ERROR(rc
);
126 /* Remember to free the msg1 blob. The contents of this
127 have been copied into cred and need freeing. */
128 data_blob_free(&msg1
);
136 perform a LDAP/SASL/SPNEGO/KRB5 bind
138 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
140 DATA_BLOB blob
= data_blob(NULL
, 0);
141 struct berval cred
, *scred
= NULL
;
142 DATA_BLOB session_key
= data_blob(NULL
, 0);
145 rc
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
, &blob
, &session_key
, 0);
148 return ADS_ERROR_KRB5(rc
);
151 /* now send the auth packet and we should be done */
152 cred
.bv_val
= (char *)blob
.data
;
153 cred
.bv_len
= blob
.length
;
155 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
157 data_blob_free(&blob
);
158 data_blob_free(&session_key
);
162 return ADS_ERROR(rc
);
166 this performs a SASL/SPNEGO bind
168 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
170 struct berval
*scred
=NULL
;
174 char *principal
= NULL
;
175 char *OIDs
[ASN1_MAX_OIDS
];
177 BOOL got_kerberos_mechanism
= False
;
180 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
182 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
183 status
= ADS_ERROR(rc
);
187 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
192 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
195 /* the server sent us the first part of the SPNEGO exchange in the negprot
197 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
198 data_blob_free(&blob
);
199 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
202 data_blob_free(&blob
);
204 /* make sure the server understands kerberos */
205 for (i
=0;OIDs
[i
];i
++) {
206 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs
[i
]));
208 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
209 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
210 got_kerberos_mechanism
= True
;
215 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal
));
218 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
219 got_kerberos_mechanism
) {
220 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
221 if (ADS_ERR_OK(status
)) {
222 SAFE_FREE(principal
);
226 status
= ADS_ERROR_KRB5(ads_kinit_password(ads
));
228 if (ADS_ERR_OK(status
)) {
229 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
232 /* only fallback to NTLMSSP if allowed */
233 if (ADS_ERR_OK(status
) ||
234 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
235 SAFE_FREE(principal
);
241 SAFE_FREE(principal
);
243 /* lets do NTLMSSP ... this has the big advantage that we don't need
244 to sync clocks, and we don't rely on special versions of the krb5
245 library for HMAC_MD4 encryption */
246 return ads_sasl_spnego_ntlmssp_bind(ads
);
253 #define MAX_GSS_PASSES 3
255 /* this performs a SASL/gssapi bind
256 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
257 is very dependent on correctly configured DNS whereas
258 this routine is much less fragile
259 see RFC2078 and RFC2222 for details
261 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
264 gss_name_t serv_name
;
265 gss_buffer_desc input_name
;
266 gss_ctx_id_t context_handle
;
267 gss_OID mech_type
= GSS_C_NULL_OID
;
268 gss_buffer_desc output_token
, input_token
;
269 uint32 ret_flags
, conf_state
;
271 struct berval
*scred
= NULL
;
279 krb5_principal principal
;
280 krb5_context ctx
= NULL
;
281 krb5_enctype enc_types
[] = {
282 #ifdef ENCTYPE_ARCFOUR_HMAC
283 ENCTYPE_ARCFOUR_HMAC
,
287 gss_OID_desc nt_principal
=
288 {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
290 /* we need to fetch a service ticket as the ldap user in the
291 servers realm, regardless of our realm */
292 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
293 krb5_init_context(&ctx
);
294 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
295 krb5_parse_name(ctx
, sname
, &principal
);
297 krb5_free_context(ctx
);
299 input_name
.value
= &principal
;
300 input_name
.length
= sizeof(principal
);
302 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
304 return ADS_ERROR_GSS(gss_rc
, minor_status
);
307 context_handle
= GSS_C_NO_CONTEXT
;
309 input_token
.value
= NULL
;
310 input_token
.length
= 0;
312 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
313 gss_rc
= gss_init_sec_context(&minor_status
,
318 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
327 if (input_token
.value
) {
328 gss_release_buffer(&minor_status
, &input_token
);
331 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
332 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
336 cred
.bv_val
= output_token
.value
;
337 cred
.bv_len
= output_token
.length
;
339 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
341 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
342 status
= ADS_ERROR(rc
);
346 if (output_token
.value
) {
347 gss_release_buffer(&minor_status
, &output_token
);
351 input_token
.value
= scred
->bv_val
;
352 input_token
.length
= scred
->bv_len
;
354 input_token
.value
= NULL
;
355 input_token
.length
= 0;
358 if (gss_rc
== 0) break;
361 gss_release_name(&minor_status
, &serv_name
);
363 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
364 (int *)&conf_state
,NULL
);
366 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
370 gss_release_buffer(&minor_status
, &input_token
);
372 p
= (uint8
*)output_token
.value
;
374 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
376 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
379 gss_release_buffer(&minor_status
, &output_token
);
381 output_token
.value
= SMB_MALLOC(strlen(ads
->config
.bind_path
) + 8);
382 p
= output_token
.value
;
384 *p
++ = 1; /* no sign & seal selection */
385 /* choose the same size as the server gave us */
386 *p
++ = max_msg_size
>>16;
387 *p
++ = max_msg_size
>>8;
389 snprintf((char *)p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
390 p
+= strlen((const char *)p
);
392 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
394 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
395 &output_token
, (int *)&conf_state
,
398 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
402 free(output_token
.value
);
404 cred
.bv_val
= input_token
.value
;
405 cred
.bv_len
= input_token
.length
;
407 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
409 status
= ADS_ERROR(rc
);
411 gss_release_buffer(&minor_status
, &input_token
);
420 /* mapping between SASL mechanisms and functions */
423 ADS_STATUS (*fn
)(ADS_STRUCT
*);
424 } sasl_mechanisms
[] = {
425 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
427 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
432 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
434 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
440 /* get a list of supported SASL mechanisms */
441 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
442 if (!ADS_ERR_OK(status
)) return status
;
444 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
446 /* try our supported mechanisms in order */
447 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
448 /* see if the server supports it */
449 for (j
=0;values
&& values
[j
];j
++) {
450 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
451 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
452 status
= sasl_mechanisms
[i
].fn(ads
);
453 ldap_value_free(values
);
460 ldap_value_free(values
);
462 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);