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
};
33 DATA_BLOB blob
, chal1
, chal2
, auth
;
35 uint8 nthash
[24], lmhash
[24], sess_key
[16];
37 struct berval cred
, *scred
;
39 extern pstring global_myname
;
42 if (!ads
->auth
.password
) {
43 /* No password, don't segfault below... */
44 return ADS_ERROR_NT(NT_STATUS_LOGON_FAILURE
);
47 neg_flags
= NTLMSSP_NEGOTIATE_UNICODE
|
48 NTLMSSP_NEGOTIATE_128
|
49 NTLMSSP_NEGOTIATE_NTLM
;
51 memset(sess_key
, 0, 16);
53 /* generate the ntlmssp negotiate packet */
54 msrpc_gen(&blob
, "CddB",
60 /* and wrap it in a SPNEGO wrapper */
61 msg1
= gen_negTokenTarg(mechs
, blob
);
62 data_blob_free(&blob
);
64 cred
.bv_val
= msg1
.data
;
65 cred
.bv_len
= msg1
.length
;
67 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
68 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
69 status
= ADS_ERROR(rc
);
73 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 /* now send the auth packet and we should be done */
110 cred
.bv_val
= auth
.data
;
111 cred
.bv_len
= auth
.length
;
113 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
115 return ADS_ERROR(rc
);
122 perform a LDAP/SASL/SPNEGO/KRB5 bind
124 static ADS_STATUS
ads_sasl_spnego_krb5_bind(ADS_STRUCT
*ads
, const char *principal
)
127 struct berval cred
, *scred
;
130 blob
= spnego_gen_negTokenTarg(principal
, ads
->auth
.time_offset
);
133 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
136 /* now send the auth packet and we should be done */
137 cred
.bv_val
= blob
.data
;
138 cred
.bv_len
= blob
.length
;
140 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", &cred
, NULL
, NULL
, &scred
);
142 data_blob_free(&blob
);
144 return ADS_ERROR(rc
);
148 this performs a SASL/SPNEGO bind
150 static ADS_STATUS
ads_sasl_spnego_bind(ADS_STRUCT
*ads
)
152 struct berval
*scred
=NULL
;
157 char *OIDs
[ASN1_MAX_OIDS
];
158 BOOL got_kerberos_mechanism
= False
;
160 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSS-SPNEGO", NULL
, NULL
, NULL
, &scred
);
162 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
163 status
= ADS_ERROR(rc
);
167 blob
= data_blob(scred
->bv_val
, scred
->bv_len
);
170 file_save("sasl_spnego.dat", blob
.data
, blob
.length
);
173 /* the server sent us the first part of the SPNEGO exchange in the negprot
175 if (!spnego_parse_negTokenInit(blob
, OIDs
, &principal
)) {
176 data_blob_free(&blob
);
177 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
180 data_blob_free(&blob
);
182 /* make sure the server understands kerberos */
183 for (i
=0;OIDs
[i
];i
++) {
184 DEBUG(3,("got OID=%s\n", OIDs
[i
]));
185 if (strcmp(OIDs
[i
], OID_KERBEROS5_OLD
) == 0 ||
186 strcmp(OIDs
[i
], OID_KERBEROS5
) == 0) {
187 got_kerberos_mechanism
= True
;
191 DEBUG(3,("got principal=%s\n", principal
));
193 if (!(ads
->auth
.flags
& ADS_AUTH_DISABLE_KERBEROS
) &&
194 got_kerberos_mechanism
&& ads_kinit_password(ads
) == 0) {
195 return ads_sasl_spnego_krb5_bind(ads
, principal
);
198 /* lets do NTLMSSP ... this has the big advantage that we don't need
199 to sync clocks, and we don't rely on special versions of the krb5
200 library for HMAC_MD4 encryption */
201 return ads_sasl_spnego_ntlmssp_bind(ads
);
208 #define MAX_GSS_PASSES 3
210 /* this performs a SASL/gssapi bind
211 we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
212 is very dependent on correctly configured DNS whereas
213 this routine is much less fragile
214 see RFC2078 and RFC2222 for details
216 static ADS_STATUS
ads_sasl_gssapi_bind(ADS_STRUCT
*ads
)
219 gss_name_t serv_name
;
220 gss_buffer_desc input_name
;
221 gss_ctx_id_t context_handle
;
222 gss_OID mech_type
= GSS_C_NULL_OID
;
223 gss_buffer_desc output_token
, input_token
;
224 OM_uint32 ret_flags
, conf_state
;
226 struct berval
*scred
;
234 krb5_principal principal
;
236 krb5_enctype enc_types
[] = {ENCTYPE_DES_CBC_MD5
, ENCTYPE_NULL
};
237 gss_OID_desc nt_principal
=
238 {10, "\052\206\110\206\367\022\001\002\002\002"};
240 /* we need to fetch a service ticket as the ldap user in the
241 servers realm, regardless of our realm */
242 asprintf(&sname
, "ldap/%s@%s", ads
->config
.ldap_server_name
, ads
->config
.realm
);
243 krb5_init_context(&ctx
);
244 krb5_set_default_tgs_ktypes(ctx
, enc_types
);
245 krb5_parse_name(ctx
, sname
, &principal
);
247 krb5_free_context(ctx
);
249 input_name
.value
= &principal
;
250 input_name
.length
= sizeof(principal
);
252 gss_rc
= gss_import_name(&minor_status
,&input_name
,&nt_principal
, &serv_name
);
254 return ADS_ERROR_GSS(gss_rc
, minor_status
);
257 context_handle
= GSS_C_NO_CONTEXT
;
259 input_token
.value
= NULL
;
260 input_token
.length
= 0;
262 for (i
=0; i
< MAX_GSS_PASSES
; i
++) {
263 gss_rc
= gss_init_sec_context(&minor_status
,
268 GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG
,
277 if (input_token
.value
) {
278 gss_release_buffer(&minor_status
, &input_token
);
281 if (gss_rc
&& gss_rc
!= GSS_S_CONTINUE_NEEDED
) {
282 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
286 cred
.bv_val
= output_token
.value
;
287 cred
.bv_len
= output_token
.length
;
289 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
291 if (rc
!= LDAP_SASL_BIND_IN_PROGRESS
) {
292 status
= ADS_ERROR(rc
);
296 if (output_token
.value
) {
297 gss_release_buffer(&minor_status
, &output_token
);
301 input_token
.value
= scred
->bv_val
;
302 input_token
.length
= scred
->bv_len
;
304 input_token
.value
= NULL
;
305 input_token
.length
= 0;
308 if (gss_rc
== 0) break;
311 gss_release_name(&minor_status
, &serv_name
);
313 gss_rc
= gss_unwrap(&minor_status
,context_handle
,&input_token
,&output_token
,
316 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
320 gss_release_buffer(&minor_status
, &input_token
);
322 p
= (uint8
*)output_token
.value
;
324 file_save("sasl_gssapi.dat", output_token
.value
, output_token
.length
);
326 max_msg_size
= (p
[1]<<16) | (p
[2]<<8) | p
[3];
329 gss_release_buffer(&minor_status
, &output_token
);
331 output_token
.value
= malloc(strlen(ads
->config
.bind_path
) + 8);
332 p
= output_token
.value
;
334 *p
++ = 1; /* no sign & seal selection */
335 /* choose the same size as the server gave us */
336 *p
++ = max_msg_size
>>16;
337 *p
++ = max_msg_size
>>8;
339 snprintf(p
, strlen(ads
->config
.bind_path
)+4, "dn:%s", ads
->config
.bind_path
);
342 output_token
.length
= PTR_DIFF(p
, output_token
.value
);
344 gss_rc
= gss_wrap(&minor_status
, context_handle
,0,GSS_C_QOP_DEFAULT
,
345 &output_token
, &conf_state
,
348 status
= ADS_ERROR_GSS(gss_rc
, minor_status
);
352 free(output_token
.value
);
354 cred
.bv_val
= input_token
.value
;
355 cred
.bv_len
= input_token
.length
;
357 rc
= ldap_sasl_bind_s(ads
->ld
, NULL
, "GSSAPI", &cred
, NULL
, NULL
,
359 status
= ADS_ERROR(rc
);
361 gss_release_buffer(&minor_status
, &input_token
);
368 /* mapping between SASL mechanisms and functions */
371 ADS_STATUS (*fn
)(ADS_STRUCT
*);
372 } sasl_mechanisms
[] = {
373 {"GSS-SPNEGO", ads_sasl_spnego_bind
},
375 {"GSSAPI", ads_sasl_gssapi_bind
}, /* doesn't work with .NET RC1. No idea why */
380 ADS_STATUS
ads_sasl_bind(ADS_STRUCT
*ads
)
382 const char *attrs
[] = {"supportedSASLMechanisms", NULL
};
388 /* get a list of supported SASL mechanisms */
389 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
390 if (!ADS_ERR_OK(status
)) return status
;
392 values
= ldap_get_values(ads
->ld
, res
, "supportedSASLMechanisms");
394 /* try our supported mechanisms in order */
395 for (i
=0;sasl_mechanisms
[i
].name
;i
++) {
396 /* see if the server supports it */
397 for (j
=0;values
&& values
[j
];j
++) {
398 if (strcmp(values
[j
], sasl_mechanisms
[i
].name
) == 0) {
399 DEBUG(4,("Found SASL mechanism %s\n", values
[j
]));
400 status
= sasl_mechanisms
[i
].fn(ads
);
401 ldap_value_free(values
);
408 ldap_value_free(values
);
410 return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED
);