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.
21 #define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
28 perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
29 we fit on one socket??)
31 static ADS_STATUS
ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT
*ads
)
33 const char *mechs
[] = {OID_NTLMSSP
, NULL
};
34 DATA_BLOB msg1
= data_blob(NULL
, 0);
35 DATA_BLOB blob
, chal1
, chal2
, auth
;
37 uint8 nthash
[24], lmhash
[24], sess_key
[16];
39 struct berval cred
, *scred
= NULL
;
43 if (!ads
->auth
.password
) {
44 /* No password, don't segfault below... */
45 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE
);
48 neg_flags
= NTLMSSP_NEGOTIATE_UNICODE
|
49 NTLMSSP_NEGOTIATE_128
|
50 NTLMSSP_NEGOTIATE_NTLM
;
52 memset(sess_key
, 0, 16);
54 /* generate the ntlmssp negotiate packet */
55 msrpc_gen(&blob
, "CddB",
61 /* and wrap it in a SPNEGO wrapper */
62 msg1
= gen_negTokenTarg(mechs
, blob
);
63 data_blob_free(&blob
);
65 cred
.bv_val
= (char *)msg1
.data
;
66 cred
.bv_len
= msg1
.length
;
68 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
69 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
70 status
= ADS_ERROR(rc
);
74 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
77 /* the server gives us back two challenges */
78 if (!spnego_parse_challenge(blob
, &chal1
, &chal2
)) {
79 DEBUG(3,("Failed to parse challenges\n"));
80 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
84 data_blob_free(&blob
);
86 /* encrypt the password with the challenge */
87 memcpy(challenge
, chal1
.data
+ 24, 8);
88 SMBencrypt(ads
->auth
.password
, challenge
,lmhash
);
89 SMBNTencrypt(ads
->auth
.password
, challenge
,nthash
);
91 data_blob_free(&chal1
);
92 data_blob_free(&chal2
);
94 /* this generates the actual auth packet */
95 msrpc_gen(&blob
, "CdBBUUUBd",
106 /* wrap it in SPNEGO */
107 auth
= spnego_gen_auth(blob
);
109 data_blob_free(&blob
);
111 /* Remember to free the msg1 blob. The contents of this
112 have been copied into cred and need freeing before reassignment. */
113 data_blob_free(&msg1
);
115 /* now send the auth packet and we should be done */
116 cred
.bv_val
= (char *)auth
.data
;
117 cred
.bv_len
= auth
.length
;
119 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
122 data_blob_free(&auth
);
124 return ADS_ERROR(rc
);
128 /* Remember to free the msg1 blob. The contents of this
129 have been copied into cred and need freeing. */
130 data_blob_free(&msg1
);
138 perform a LDAP/SASL/SPNEGO/KRB5 bind
140 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
142 DATA_BLOB blob
= data_blob(NULL
, 0);
143 struct berval cred
, *scred
= NULL
;
144 DATA_BLOB session_key
= data_blob(NULL
, 0);
147 rc
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
, &blob
, &session_key
);
150 return ADS_ERROR_KRB5(rc
);
153 /* now send the auth packet and we should be done */
154 cred
.bv_val
= (char *)blob
.data
;
155 cred
.bv_len
= blob
.length
;
157 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
159 data_blob_free(&blob
);
160 data_blob_free(&session_key
);
164 return ADS_ERROR(rc
);
168 this performs a SASL/SPNEGO bind
170 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
172 struct berval
*scred
=NULL
;
176 char *principal
= NULL
;
177 char *OIDs
[ASN1_MAX_OIDS
];
179 BOOL got_kerberos_mechanism
= False
;
182 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
184 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
185 status
= ADS_ERROR(rc
);
189 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
194 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
197 /* the server sent us the first part of the SPNEGO exchange in the negprot
199 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
200 data_blob_free(&blob
);
201 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
204 data_blob_free(&blob
);
206 /* make sure the server understands kerberos */
207 for (i
=0;OIDs
[i
];i
++) {
208 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs
[i
]));
210 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
211 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
212 got_kerberos_mechanism
= True
;
217 DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal
));
220 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
221 got_kerberos_mechanism
) {
222 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
223 if (ADS_ERR_OK(status
)) {
224 SAFE_FREE(principal
);
228 status
= ADS_ERROR_KRB5(ads_kinit_password(ads
));
230 if (ADS_ERR_OK(status
)) {
231 status
= ads_sasl_spnego_krb5_bind(ads
, principal
);
234 /* only fallback to NTLMSSP if allowed */
235 if (ADS_ERR_OK(status
) ||
236 !(ads
->auth
.flags
& ADS_AUTH_ALLOW_NTLMSSP
)) {
237 SAFE_FREE(principal
);
243 SAFE_FREE(principal
);
245 /* lets do NTLMSSP ... this has the big advantage that we don't need
246 to sync clocks, and we don't rely on special versions of the krb5
247 library for HMAC_MD4 encryption */
248 return ads_sasl_spnego_ntlmssp_bind(ads
);
255 #define MAX_GSS_PASSES 3
257 /* this performs a SASL/gssapi bind
258 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
259 is very dependent on correctly configured DNS whereas
260 this routine is much less fragile
261 see RFC2078 and RFC2222 for details
263 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
266 gss_name_t serv_name
;
267 gss_buffer_desc input_name
;
268 gss_ctx_id_t context_handle
;
269 gss_OID mech_type
= GSS_C_NULL_OID
;
270 gss_buffer_desc output_token
, input_token
;
271 uint32 ret_flags
, conf_state
;
273 struct berval
*scred
= NULL
;
281 krb5_principal principal
;
282 krb5_context ctx
= NULL
;
283 krb5_enctype enc_types
[] = {
284 #ifdef ENCTYPE_ARCFOUR_HMAC
285 ENCTYPE_ARCFOUR_HMAC
,
289 gss_OID_desc nt_principal
=
290 {10, CONST_DISCARD(char *,
291 "\052\206\110\206\367\022\001\002\002\002")};
293 /* we need to fetch a service ticket as the ldap user in the
294 servers realm, regardless of our realm */
295 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
296 krb5_init_context(&ctx
);
297 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
298 krb5_parse_name(ctx
, sname
, &principal
);
300 krb5_free_context(ctx
);
302 input_name
.value
= &principal
;
303 input_name
.length
= sizeof(principal
);
305 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
307 return ADS_ERROR_GSS(gss_rc
, minor_status
);
310 context_handle
= GSS_C_NO_CONTEXT
;
312 input_token
.value
= NULL
;
313 input_token
.length
= 0;
315 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
316 gss_rc
= gss_init_sec_context(&minor_status
,
321 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
330 if (input_token
.value
) {
331 gss_release_buffer(&minor_status
, &input_token
);
334 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
335 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
339 cred
.bv_val
= output_token
.value
;
340 cred
.bv_len
= output_token
.length
;
342 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
344 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
345 status
= ADS_ERROR(rc
);
349 if (output_token
.value
) {
350 gss_release_buffer(&minor_status
, &output_token
);
354 input_token
.value
= scred
->bv_val
;
355 input_token
.length
= scred
->bv_len
;
357 input_token
.value
= NULL
;
358 input_token
.length
= 0;
361 if (gss_rc
== 0) break;
364 gss_release_name(&minor_status
, &serv_name
);
366 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
367 (int *)&conf_state
,NULL
);
369 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
373 gss_release_buffer(&minor_status
, &input_token
);
375 p
= (uint8
*)output_token
.value
;
377 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
379 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
382 gss_release_buffer(&minor_status
, &output_token
);
384 output_token
.value
= SMB_MALLOC(strlen(ads
->config
.bind_path
) + 8);
385 p
= output_token
.value
;
387 *p
++ = 1; /* no sign & seal selection */
388 /* choose the same size as the server gave us */
389 *p
++ = max_msg_size
>>16;
390 *p
++ = max_msg_size
>>8;
392 snprintf((char *)p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
393 p
+= strlen((const char *)p
);
395 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
397 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
398 &output_token
, (int *)&conf_state
,
401 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
405 free(output_token
.value
);
407 cred
.bv_val
= input_token
.value
;
408 cred
.bv_len
= input_token
.length
;
410 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
412 status
= ADS_ERROR(rc
);
414 gss_release_buffer(&minor_status
, &input_token
);
423 /* mapping between SASL mechanisms and functions */
426 ADS_STATUS (*fn
)(ADS_STRUCT
*);
427 } sasl_mechanisms
[] = {
428 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
430 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
435 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
437 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
443 /* get a list of supported SASL mechanisms */
444 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
445 if (!ADS_ERR_OK(status
)) return status
;
447 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
449 /* try our supported mechanisms in order */
450 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
451 /* see if the server supports it */
452 for (j
=0;values
&& values
[j
];j
++) {
453 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
454 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
455 status
= sasl_mechanisms
[i
].fn(ads
);
456 ldap_value_free(values
);
463 ldap_value_free(values
);
465 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);