r17899: Fix Stanford checker bug - possible null deref.
[metze/samba/wip.git] / source3 / libads / sasl.c
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 #ifdef HAVE_LDAP
24
25 /* 
26    perform a LDAP/SASL/SPNEGO/NTLMSSP bind (just how many layers can
27    we fit on one socket??)
28 */
29 static ADS_STATUS ads_sasl_spnego_ntlmssp_bind(ADS_STRUCT *ads)
30 {
31         DATA_BLOB msg1 = data_blob(NULL, 0);
32         DATA_BLOB blob = data_blob(NULL, 0);
33         DATA_BLOB blob_in = data_blob(NULL, 0);
34         DATA_BLOB blob_out = data_blob(NULL, 0);
35         struct berval cred, *scred = NULL;
36         int rc;
37         NTSTATUS nt_status;
38         int turn = 1;
39
40         struct ntlmssp_state *ntlmssp_state;
41
42         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
43                 return ADS_ERROR_NT(nt_status);
44         }
45         ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
46
47         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, ads->auth.user_name))) {
48                 return ADS_ERROR_NT(nt_status);
49         }
50         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, ads->auth.realm))) {
51                 return ADS_ERROR_NT(nt_status);
52         }
53         if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, ads->auth.password))) {
54                 return ADS_ERROR_NT(nt_status);
55         }
56
57         blob_in = data_blob(NULL, 0);
58
59         do {
60                 nt_status = ntlmssp_update(ntlmssp_state, 
61                                            blob_in, &blob_out);
62                 data_blob_free(&blob_in);
63                 if ((NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) 
64                      || NT_STATUS_IS_OK(nt_status))
65                     && blob_out.length) {
66                         if (turn == 1) {
67                                 /* and wrap it in a SPNEGO wrapper */
68                                 msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
69                         } else {
70                                 /* wrap it in SPNEGO */
71                                 msg1 = spnego_gen_auth(blob_out);
72                         }
73
74                         data_blob_free(&blob_out);
75
76                         cred.bv_val = (char *)msg1.data;
77                         cred.bv_len = msg1.length;
78                         scred = NULL;
79                         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
80                         data_blob_free(&msg1);
81                         if ((rc != LDAP_SASL_BIND_IN_PROGRESS) && (rc != 0)) {
82                                 if (scred) {
83                                         ber_bvfree(scred);
84                                 }
85
86                                 ntlmssp_end(&ntlmssp_state);
87                                 return ADS_ERROR(rc);
88                         }
89                         if (scred) {
90                                 blob = data_blob(scred->bv_val, scred->bv_len);
91                                 ber_bvfree(scred);
92                         } else {
93                                 blob = data_blob(NULL, 0);
94                         }
95
96                 } else {
97
98                         ntlmssp_end(&ntlmssp_state);
99                         data_blob_free(&blob_out);
100                         return ADS_ERROR_NT(nt_status);
101                 }
102                 
103                 if ((turn == 1) && 
104                     (rc == LDAP_SASL_BIND_IN_PROGRESS)) {
105                         DATA_BLOB tmp_blob = data_blob(NULL, 0);
106                         /* the server might give us back two challenges */
107                         if (!spnego_parse_challenge(blob, &blob_in, 
108                                                     &tmp_blob)) {
109
110                                 ntlmssp_end(&ntlmssp_state);
111                                 data_blob_free(&blob);
112                                 DEBUG(3,("Failed to parse challenges\n"));
113                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
114                         }
115                         data_blob_free(&tmp_blob);
116                 } else if (rc == LDAP_SASL_BIND_IN_PROGRESS) {
117                         if (!spnego_parse_auth_response(blob, nt_status, 
118                                                         &blob_in)) {
119
120                                 ntlmssp_end(&ntlmssp_state);
121                                 data_blob_free(&blob);
122                                 DEBUG(3,("Failed to parse auth response\n"));
123                                 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
124                         }
125                 }
126                 data_blob_free(&blob);
127                 data_blob_free(&blob_out);
128                 turn++;
129         } while (rc == LDAP_SASL_BIND_IN_PROGRESS && !NT_STATUS_IS_OK(nt_status));
130         
131         /* we have a reference conter on ntlmssp_state, if we are signing
132            then the state will be kept by the signing engine */
133
134         ntlmssp_end(&ntlmssp_state);
135
136         return ADS_ERROR(rc);
137 }
138
139 /* 
140    perform a LDAP/SASL/SPNEGO/KRB5 bind
141 */
142 static ADS_STATUS ads_sasl_spnego_krb5_bind(ADS_STRUCT *ads, const char *principal)
143 {
144         DATA_BLOB blob = data_blob(NULL, 0);
145         struct berval cred, *scred = NULL;
146         DATA_BLOB session_key = data_blob(NULL, 0);
147         int rc;
148
149         rc = spnego_gen_negTokenTarg(principal, ads->auth.time_offset, &blob, &session_key, 0);
150
151         if (rc) {
152                 return ADS_ERROR_KRB5(rc);
153         }
154
155         /* now send the auth packet and we should be done */
156         cred.bv_val = (char *)blob.data;
157         cred.bv_len = blob.length;
158
159         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred);
160
161         data_blob_free(&blob);
162         data_blob_free(&session_key);
163         if(scred)
164                 ber_bvfree(scred);
165
166         return ADS_ERROR(rc);
167 }
168
169 /* 
170    this performs a SASL/SPNEGO bind
171 */
172 static ADS_STATUS ads_sasl_spnego_bind(ADS_STRUCT *ads)
173 {
174         struct berval *scred=NULL;
175         int rc, i;
176         ADS_STATUS status;
177         DATA_BLOB blob;
178         char *principal = NULL;
179         char *OIDs[ASN1_MAX_OIDS];
180 #ifdef HAVE_KRB5
181         BOOL got_kerberos_mechanism = False;
182 #endif
183
184         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSS-SPNEGO", NULL, NULL, NULL, &scred);
185
186         if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
187                 status = ADS_ERROR(rc);
188                 goto failed;
189         }
190
191         blob = data_blob(scred->bv_val, scred->bv_len);
192
193         ber_bvfree(scred);
194
195 #if 0
196         file_save("sasl_spnego.dat", blob.data, blob.length);
197 #endif
198
199         /* the server sent us the first part of the SPNEGO exchange in the negprot 
200            reply */
201         if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
202                 data_blob_free(&blob);
203                 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
204                 goto failed;
205         }
206         data_blob_free(&blob);
207
208         /* make sure the server understands kerberos */
209         for (i=0;OIDs[i];i++) {
210                 DEBUG(3,("ads_sasl_spnego_bind: got OID=%s\n", OIDs[i]));
211 #ifdef HAVE_KRB5
212                 if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
213                     strcmp(OIDs[i], OID_KERBEROS5) == 0) {
214                         got_kerberos_mechanism = True;
215                 }
216 #endif
217                 free(OIDs[i]);
218         }
219         DEBUG(3,("ads_sasl_spnego_bind: got server principal name =%s\n", principal));
220
221 #ifdef HAVE_KRB5
222         if (!(ads->auth.flags & ADS_AUTH_DISABLE_KERBEROS) &&
223             got_kerberos_mechanism) {
224                 status = ads_sasl_spnego_krb5_bind(ads, principal);
225                 if (ADS_ERR_OK(status)) {
226                         SAFE_FREE(principal);
227                         return status;
228                 }
229
230                 status = ADS_ERROR_KRB5(ads_kinit_password(ads)); 
231
232                 if (ADS_ERR_OK(status)) {
233                         status = ads_sasl_spnego_krb5_bind(ads, principal);
234                 }
235
236                 /* only fallback to NTLMSSP if allowed */
237                 if (ADS_ERR_OK(status) || 
238                     !(ads->auth.flags & ADS_AUTH_ALLOW_NTLMSSP)) {
239                         SAFE_FREE(principal);
240                         return status;
241                 }
242         }
243 #endif
244
245         SAFE_FREE(principal);
246
247         /* lets do NTLMSSP ... this has the big advantage that we don't need
248            to sync clocks, and we don't rely on special versions of the krb5 
249            library for HMAC_MD4 encryption */
250         return ads_sasl_spnego_ntlmssp_bind(ads);
251
252 failed:
253         return status;
254 }
255
256 #ifdef HAVE_GSSAPI
257 #define MAX_GSS_PASSES 3
258
259 /* this performs a SASL/gssapi bind
260    we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
261    is very dependent on correctly configured DNS whereas
262    this routine is much less fragile
263    see RFC2078 and RFC2222 for details
264 */
265 static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
266 {
267         uint32 minor_status;
268         gss_name_t serv_name;
269         gss_buffer_desc input_name;
270         gss_ctx_id_t context_handle;
271         gss_OID mech_type = GSS_C_NULL_OID;
272         gss_buffer_desc output_token, input_token;
273         uint32 ret_flags, conf_state;
274         struct berval cred;
275         struct berval *scred = NULL;
276         int i=0;
277         int gss_rc, rc;
278         uint8 *p;
279         uint32 max_msg_size = 0;
280         char *sname;
281         ADS_STATUS status;
282         krb5_principal principal;
283         krb5_context ctx = NULL;
284         krb5_enctype enc_types[] = {
285 #ifdef ENCTYPE_ARCFOUR_HMAC
286                         ENCTYPE_ARCFOUR_HMAC,
287 #endif
288                         ENCTYPE_DES_CBC_MD5,
289                         ENCTYPE_NULL};
290         gss_OID_desc nt_principal = 
291         {10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};
292
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
297         initialize_krb5_error_table();
298         status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
299         if (!ADS_ERR_OK(status)) {
300                 return status;
301         }
302         status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
303         if (!ADS_ERR_OK(status)) {
304                 return status;
305         }
306         status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
307         if (!ADS_ERR_OK(status)) {
308                 return status;
309         }
310
311         free(sname);
312         krb5_free_context(ctx); 
313
314         input_name.value = &principal;
315         input_name.length = sizeof(principal);
316
317         gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);
318         if (gss_rc) {
319                 return ADS_ERROR_GSS(gss_rc, minor_status);
320         }
321
322         context_handle = GSS_C_NO_CONTEXT;
323
324         input_token.value = NULL;
325         input_token.length = 0;
326
327         for (i=0; i < MAX_GSS_PASSES; i++) {
328                 gss_rc = gss_init_sec_context(&minor_status,
329                                           GSS_C_NO_CREDENTIAL,
330                                           &context_handle,
331                                           serv_name,
332                                           mech_type,
333                                           GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
334                                           0,
335                                           NULL,
336                                           &input_token,
337                                           NULL,
338                                           &output_token,
339                                           &ret_flags,
340                                           NULL);
341
342                 if (input_token.value) {
343                         gss_release_buffer(&minor_status, &input_token);
344                 }
345
346                 if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
347                         status = ADS_ERROR_GSS(gss_rc, minor_status);
348                         goto failed;
349                 }
350
351                 cred.bv_val = output_token.value;
352                 cred.bv_len = output_token.length;
353
354                 rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
355                                       &scred);
356                 if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
357                         status = ADS_ERROR(rc);
358                         goto failed;
359                 }
360
361                 if (output_token.value) {
362                         gss_release_buffer(&minor_status, &output_token);
363                 }
364
365                 if (scred) {
366                         input_token.value = scred->bv_val;
367                         input_token.length = scred->bv_len;
368                 } else {
369                         input_token.value = NULL;
370                         input_token.length = 0;
371                 }
372
373                 if (gss_rc == 0) break;
374         }
375
376         gss_release_name(&minor_status, &serv_name);
377
378         gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
379                             (int *)&conf_state,NULL);
380         if (gss_rc) {
381                 status = ADS_ERROR_GSS(gss_rc, minor_status);
382                 goto failed;
383         }
384
385         gss_release_buffer(&minor_status, &input_token);
386
387         p = (uint8 *)output_token.value;
388
389 #if 0
390         file_save("sasl_gssapi.dat", output_token.value, output_token.length);
391 #endif
392
393         if (p) {
394                 max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
395         }
396
397         gss_release_buffer(&minor_status, &output_token);
398
399         output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
400         p = output_token.value;
401
402         *p++ = 1; /* no sign & seal selection */
403         /* choose the same size as the server gave us */
404         *p++ = max_msg_size>>16;
405         *p++ = max_msg_size>>8;
406         *p++ = max_msg_size;
407         snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
408         p += strlen((const char *)p);
409
410         output_token.length = PTR_DIFF(p, output_token.value);
411
412         gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
413                           &output_token, (int *)&conf_state,
414                           &input_token);
415         if (gss_rc) {
416                 status = ADS_ERROR_GSS(gss_rc, minor_status);
417                 goto failed;
418         }
419
420         free(output_token.value);
421
422         cred.bv_val = input_token.value;
423         cred.bv_len = input_token.length;
424
425         rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
426                               &scred);
427         status = ADS_ERROR(rc);
428
429         gss_release_buffer(&minor_status, &input_token);
430
431 failed:
432         if(scred)
433                 ber_bvfree(scred);
434         return status;
435 }
436 #endif /* HAVE_GGSAPI */
437
438 /* mapping between SASL mechanisms and functions */
439 static struct {
440         const char *name;
441         ADS_STATUS (*fn)(ADS_STRUCT *);
442 } sasl_mechanisms[] = {
443         {"GSS-SPNEGO", ads_sasl_spnego_bind},
444 #ifdef HAVE_GSSAPI
445         {"GSSAPI", ads_sasl_gssapi_bind}, /* doesn't work with .NET RC1. No idea why */
446 #endif
447         {NULL, NULL}
448 };
449
450 ADS_STATUS ads_sasl_bind(ADS_STRUCT *ads)
451 {
452         const char *attrs[] = {"supportedSASLMechanisms", NULL};
453         char **values;
454         ADS_STATUS status;
455         int i, j;
456         void *res;
457
458         /* get a list of supported SASL mechanisms */
459         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
460         if (!ADS_ERR_OK(status)) return status;
461
462         values = ldap_get_values(ads->ld, res, "supportedSASLMechanisms");
463
464         /* try our supported mechanisms in order */
465         for (i=0;sasl_mechanisms[i].name;i++) {
466                 /* see if the server supports it */
467                 for (j=0;values && values[j];j++) {
468                         if (strcmp(values[j], sasl_mechanisms[i].name) == 0) {
469                                 DEBUG(4,("Found SASL mechanism %s\n", values[j]));
470                                 status = sasl_mechanisms[i].fn(ads);
471                                 ldap_value_free(values);
472                                 ldap_msgfree(res);
473                                 return status;
474                         }
475                 }
476         }
477
478         ldap_value_free(values);
479         ldap_msgfree(res);
480         return ADS_ERROR(LDAP_AUTH_METHOD_NOT_SUPPORTED);
481 }
482
483 #endif /* HAVE_LDAP */
484