~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/libcli/ldap/ldap_bind.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   Unix SMB/CIFS mplementation.
 
3
 
 
4
   LDAP bind calls
 
5
   
 
6
   Copyright (C) Andrew Tridgell  2005
 
7
   Copyright (C) Volker Lendecke  2004
 
8
    
 
9
   This program is free software; you can redistribute it and/or modify
 
10
   it under the terms of the GNU General Public License as published by
 
11
   the Free Software Foundation; either version 3 of the License, or
 
12
   (at your option) any later version.
 
13
   
 
14
   This program is distributed in the hope that it will be useful,
 
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
   GNU General Public License for more details.
 
18
   
 
19
   You should have received a copy of the GNU General Public License
 
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
21
   
 
22
*/
 
23
 
 
24
#include "includes.h"
 
25
#include "libcli/ldap/ldap.h"
 
26
#include "libcli/ldap/ldap_proto.h"
 
27
#include "libcli/ldap/ldap_client.h"
 
28
#include "lib/tls/tls.h"
 
29
#include "auth/gensec/gensec.h"
 
30
#include "auth/credentials/credentials.h"
 
31
#include "lib/stream/packet.h"
 
32
#include "param/param.h"
 
33
 
 
34
struct ldap_simple_creds {
 
35
        const char *dn;
 
36
        const char *pw;
 
37
};
 
38
 
 
39
_PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn)
 
40
{
 
41
        NTSTATUS status;
 
42
        struct ldap_simple_creds *creds;
 
43
 
 
44
        switch (conn->bind.type) {
 
45
        case LDAP_BIND_SASL:
 
46
                status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds,
 
47
                                        conn->lp_ctx);
 
48
                break;
 
49
                
 
50
        case LDAP_BIND_SIMPLE:
 
51
                creds = (struct ldap_simple_creds *)conn->bind.creds;
 
52
 
 
53
                if (creds == NULL) {
 
54
                        return NT_STATUS_UNSUCCESSFUL;
 
55
                }
 
56
 
 
57
                status = ldap_bind_simple(conn, creds->dn, creds->pw);
 
58
                break;
 
59
 
 
60
        default:
 
61
                return NT_STATUS_UNSUCCESSFUL;
 
62
        }
 
63
 
 
64
        return status;
 
65
}
 
66
 
 
67
 
 
68
static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn, 
 
69
                                                     const char *dn, const char *pw)
 
70
{
 
71
        struct ldap_message *res;
 
72
 
 
73
        res = new_ldap_message(conn);
 
74
        if (!res) {
 
75
                return NULL;
 
76
        }
 
77
 
 
78
        res->type = LDAP_TAG_BindRequest;
 
79
        res->r.BindRequest.version = 3;
 
80
        res->r.BindRequest.dn = talloc_strdup(res, dn);
 
81
        res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
 
82
        res->r.BindRequest.creds.password = talloc_strdup(res, pw);
 
83
        res->controls = NULL;
 
84
 
 
85
        return res;
 
86
}
 
87
 
 
88
 
 
89
/*
 
90
  perform a simple username/password bind
 
91
*/
 
92
_PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn, 
 
93
                          const char *userdn, const char *password)
 
94
{
 
95
        struct ldap_request *req;
 
96
        struct ldap_message *msg;
 
97
        const char *dn, *pw;
 
98
        NTSTATUS status;
 
99
 
 
100
        if (conn == NULL) {
 
101
                return NT_STATUS_INVALID_CONNECTION;
 
102
        }
 
103
 
 
104
        if (userdn) {
 
105
                dn = userdn;
 
106
        } else {
 
107
                if (conn->auth_dn) {
 
108
                        dn = conn->auth_dn;
 
109
                } else {
 
110
                        dn = "";
 
111
                }
 
112
        }
 
113
 
 
114
        if (password) {
 
115
                pw = password;
 
116
        } else {
 
117
                if (conn->simple_pw) {
 
118
                        pw = conn->simple_pw;
 
119
                } else {
 
120
                        pw = "";
 
121
                }
 
122
        }
 
123
 
 
124
        msg = new_ldap_simple_bind_msg(conn, dn, pw);
 
125
        NT_STATUS_HAVE_NO_MEMORY(msg);
 
126
 
 
127
        /* send the request */
 
128
        req = ldap_request_send(conn, msg);
 
129
        talloc_free(msg);
 
130
        NT_STATUS_HAVE_NO_MEMORY(req);
 
131
 
 
132
        /* wait for replies */
 
133
        status = ldap_request_wait(req);
 
134
        if (!NT_STATUS_IS_OK(status)) {
 
135
                talloc_free(req);
 
136
                return status;
 
137
        }
 
138
 
 
139
        /* check its a valid reply */
 
140
        msg = req->replies[0];
 
141
        if (msg->type != LDAP_TAG_BindResponse) {
 
142
                talloc_free(req);
 
143
                return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 
144
        }
 
145
 
 
146
        status = ldap_check_response(conn, &msg->r.BindResponse.response);
 
147
 
 
148
        talloc_free(req);
 
149
 
 
150
        if (NT_STATUS_IS_OK(status)) {
 
151
                struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds);
 
152
                if (creds == NULL) {
 
153
                        return NT_STATUS_NO_MEMORY;
 
154
                }
 
155
                creds->dn = talloc_strdup(creds, dn);
 
156
                creds->pw = talloc_strdup(creds, pw);
 
157
                if (creds->dn == NULL || creds->pw == NULL) {
 
158
                        return NT_STATUS_NO_MEMORY;
 
159
                }
 
160
                conn->bind.type = LDAP_BIND_SIMPLE;
 
161
                conn->bind.creds = creds;
 
162
        }
 
163
 
 
164
        return status;
 
165
}
 
166
 
 
167
 
 
168
static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn, 
 
169
                                                   const char *sasl_mechanism, 
 
170
                                                   DATA_BLOB *secblob)
 
171
{
 
172
        struct ldap_message *res;
 
173
 
 
174
        res = new_ldap_message(conn);
 
175
        if (!res) {
 
176
                return NULL;
 
177
        }
 
178
 
 
179
        res->type = LDAP_TAG_BindRequest;
 
180
        res->r.BindRequest.version = 3;
 
181
        res->r.BindRequest.dn = "";
 
182
        res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
 
183
        res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism);
 
184
        if (secblob) {
 
185
                res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB);
 
186
                if (!res->r.BindRequest.creds.SASL.secblob) {
 
187
                        talloc_free(res);
 
188
                        return NULL;
 
189
                }
 
190
                *res->r.BindRequest.creds.SASL.secblob = *secblob;
 
191
        } else {
 
192
                res->r.BindRequest.creds.SASL.secblob = NULL;
 
193
        }
 
194
        res->controls = NULL;
 
195
 
 
196
        return res;
 
197
}
 
198
 
 
199
 
 
200
/*
 
201
  perform a sasl bind using the given credentials
 
202
*/
 
203
_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
 
204
                        struct cli_credentials *creds,
 
205
                        struct loadparm_context *lp_ctx)
 
206
{
 
207
        NTSTATUS status;
 
208
        TALLOC_CTX *tmp_ctx = NULL;
 
209
 
 
210
        DATA_BLOB input = data_blob(NULL, 0);
 
211
        DATA_BLOB output = data_blob(NULL, 0);
 
212
 
 
213
        struct ldap_message **sasl_mechs_msgs;
 
214
        struct ldap_SearchResEntry *search;
 
215
        int count, i;
 
216
 
 
217
        const char **sasl_names;
 
218
        uint32_t old_gensec_features;
 
219
        static const char *supported_sasl_mech_attrs[] = {
 
220
                "supportedSASLMechanisms", 
 
221
                NULL 
 
222
        };
 
223
 
 
224
        gensec_init(lp_ctx);
 
225
 
 
226
        status = gensec_client_start(conn, &conn->gensec,
 
227
                                     conn->event.event_ctx, 
 
228
                                     lp_gensec_settings(conn, lp_ctx));
 
229
        if (!NT_STATUS_IS_OK(status)) {
 
230
                DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
 
231
                goto failed;
 
232
        }
 
233
 
 
234
        /* require Kerberos SIGN/SEAL only if we don't use SSL
 
235
         * Windows seem not to like double encryption */
 
236
        old_gensec_features = cli_credentials_get_gensec_features(creds);
 
237
        if (tls_enabled(conn->sock)) {
 
238
                cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL));
 
239
        }
 
240
 
 
241
        /* this call also sets the gensec_want_features */
 
242
        status = gensec_set_credentials(conn->gensec, creds);
 
243
        if (!NT_STATUS_IS_OK(status)) {
 
244
                DEBUG(1, ("Failed to set GENSEC creds: %s\n", 
 
245
                          nt_errstr(status)));
 
246
                goto failed;
 
247
        }
 
248
 
 
249
        /* reset the original gensec_features (on the credentials
 
250
         * context, so we don't tatoo it ) */
 
251
        cli_credentials_set_gensec_features(creds, old_gensec_features);
 
252
 
 
253
        if (conn->host) {
 
254
                status = gensec_set_target_hostname(conn->gensec, conn->host);
 
255
                if (!NT_STATUS_IS_OK(status)) {
 
256
                        DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
 
257
                                  nt_errstr(status)));
 
258
                        goto failed;
 
259
                }
 
260
        }
 
261
 
 
262
        status = gensec_set_target_service(conn->gensec, "ldap");
 
263
        if (!NT_STATUS_IS_OK(status)) {
 
264
                DEBUG(1, ("Failed to set GENSEC target service: %s\n", 
 
265
                          nt_errstr(status)));
 
266
                goto failed;
 
267
        }
 
268
 
 
269
        status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs, 
 
270
                              false, NULL, NULL, &sasl_mechs_msgs);
 
271
        if (!NT_STATUS_IS_OK(status)) {
 
272
                DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n", 
 
273
                          nt_errstr(status)));
 
274
                goto failed;
 
275
        }
 
276
        
 
277
        count = ildap_count_entries(conn, sasl_mechs_msgs);
 
278
        if (count != 1) {
 
279
                DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
 
280
                          count));
 
281
                goto failed;
 
282
        }
 
283
 
 
284
        tmp_ctx = talloc_new(conn);
 
285
        if (tmp_ctx == NULL) goto failed;
 
286
 
 
287
        search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
 
288
        if (search->num_attributes != 1) {
 
289
                DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
 
290
                          search->num_attributes));
 
291
                goto failed;
 
292
        }
 
293
 
 
294
        sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
 
295
        if (!sasl_names) {
 
296
                DEBUG(1, ("talloc_arry(char *, %d) failed\n",
 
297
                          count));
 
298
                goto failed;
 
299
        }
 
300
                
 
301
        for (i=0; i<search->attributes[0].num_values; i++) {
 
302
                sasl_names[i] = (const char *)search->attributes[0].values[i].data;
 
303
        }
 
304
        sasl_names[i] = NULL;
 
305
        
 
306
        status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
 
307
        if (!NT_STATUS_IS_OK(status)) {
 
308
                DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
 
309
                          count, nt_errstr(status)));
 
310
                goto failed;
 
311
        }
 
312
 
 
313
        while (1) {
 
314
                NTSTATUS gensec_status;
 
315
                struct ldap_message *response;
 
316
                struct ldap_message *msg;
 
317
                struct ldap_request *req;
 
318
                int result = LDAP_OTHER;
 
319
        
 
320
                status = gensec_update(conn->gensec, tmp_ctx,
 
321
                                       input,
 
322
                                       &output);
 
323
                /* The status value here, from GENSEC is vital to the security
 
324
                 * of the system.  Even if the other end accepts, if GENSEC
 
325
                 * claims 'MORE_PROCESSING_REQUIRED' then you must keep
 
326
                 * feeding it blobs, or else the remote host/attacker might
 
327
                 * avoid mutal authentication requirements.
 
328
                 *
 
329
                 * Likewise, you must not feed GENSEC too much (after the OK),
 
330
                 * it doesn't like that either
 
331
                 */
 
332
 
 
333
                gensec_status = status;
 
334
 
 
335
                if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && 
 
336
                    !NT_STATUS_IS_OK(status)) {
 
337
                        break;
 
338
                }
 
339
                if (NT_STATUS_IS_OK(status) && output.length == 0) {
 
340
                        break;
 
341
                }
 
342
 
 
343
                /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
 
344
                msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
 
345
                if (msg == NULL) {
 
346
                        status = NT_STATUS_NO_MEMORY;
 
347
                        goto failed;
 
348
                }
 
349
 
 
350
                req = ldap_request_send(conn, msg);
 
351
                if (req == NULL) {
 
352
                        status = NT_STATUS_NO_MEMORY;
 
353
                        goto failed;
 
354
                }
 
355
                talloc_steal(tmp_ctx, req);
 
356
 
 
357
                status = ldap_result_n(req, 0, &response);
 
358
                if (!NT_STATUS_IS_OK(status)) {
 
359
                        goto failed;
 
360
                }
 
361
                
 
362
                if (response->type != LDAP_TAG_BindResponse) {
 
363
                        status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 
364
                        goto failed;
 
365
                }
 
366
 
 
367
                result = response->r.BindResponse.response.resultcode;
 
368
 
 
369
                if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
 
370
                        status = ldap_check_response(conn, 
 
371
                                                     &response->r.BindResponse.response);
 
372
                        break;
 
373
                }
 
374
 
 
375
                /* This is where we check if GENSEC wanted to be fed more data */
 
376
                if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 
377
                        break;
 
378
                }
 
379
                if (response->r.BindResponse.SASL.secblob) {
 
380
                        input = *response->r.BindResponse.SASL.secblob;
 
381
                } else {
 
382
                        input = data_blob(NULL, 0);
 
383
                }
 
384
        }
 
385
 
 
386
        talloc_free(tmp_ctx);
 
387
 
 
388
        if (NT_STATUS_IS_OK(status)) {
 
389
                struct socket_context *sasl_socket;
 
390
                status = gensec_socket_init(conn->gensec, 
 
391
                                            conn,
 
392
                                            conn->sock,
 
393
                                            conn->event.event_ctx, 
 
394
                                            ldap_read_io_handler,
 
395
                                            conn,
 
396
                                            &sasl_socket);
 
397
                if (!NT_STATUS_IS_OK(status)) goto failed;
 
398
 
 
399
                conn->sock = sasl_socket;
 
400
                packet_set_socket(conn->packet, conn->sock);
 
401
 
 
402
                conn->bind.type = LDAP_BIND_SASL;
 
403
                conn->bind.creds = creds;
 
404
        }
 
405
 
 
406
        return status;
 
407
 
 
408
failed:
 
409
        talloc_free(tmp_ctx);
 
410
        talloc_free(conn->gensec);
 
411
        conn->gensec = NULL;
 
412
        return status;
 
413
}