~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/auth/gensec/cyrus_sasl.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 implementation.
 
3
 
 
4
   Connect GENSEC to an external SASL lib
 
5
 
 
6
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
 
7
   
 
8
   This program is free software; you can redistribute it and/or modify
 
9
   it under the terms of the GNU General Public License as published by
 
10
   the Free Software Foundation; either version 3 of the License, or
 
11
   (at your option) any later version.
 
12
   
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
   GNU General Public License for more details.
 
17
   
 
18
   You should have received a copy of the GNU General Public License
 
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include "includes.h"
 
23
#include "auth/auth.h"
 
24
#include "auth/credentials/credentials.h"
 
25
#include "auth/gensec/gensec.h"
 
26
#include "auth/gensec/gensec_proto.h"
 
27
#include "lib/socket/socket.h"
 
28
#include <sasl/sasl.h>
 
29
 
 
30
struct gensec_sasl_state {
 
31
        sasl_conn_t *conn;
 
32
        int step;
 
33
};
 
34
 
 
35
static NTSTATUS sasl_nt_status(int sasl_ret) 
 
36
{
 
37
        switch (sasl_ret) {
 
38
        case SASL_CONTINUE:
 
39
                return NT_STATUS_MORE_PROCESSING_REQUIRED;
 
40
        case SASL_NOMEM:
 
41
                return NT_STATUS_NO_MEMORY;
 
42
        case SASL_BADPARAM:
 
43
        case SASL_NOMECH:
 
44
                return NT_STATUS_INVALID_PARAMETER;
 
45
        case SASL_BADMAC:
 
46
                return NT_STATUS_ACCESS_DENIED;
 
47
        case SASL_OK:
 
48
                return NT_STATUS_OK;
 
49
        default:
 
50
                return NT_STATUS_UNSUCCESSFUL;
 
51
        }
 
52
}
 
53
 
 
54
static int gensec_sasl_get_user(void *context, int id,
 
55
                                const char **result, unsigned *len)
 
56
{
 
57
        struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
 
58
        const char *username = cli_credentials_get_username(gensec_get_credentials(gensec_security));
 
59
        if (id != SASL_CB_USER && id != SASL_CB_AUTHNAME) {
 
60
                return SASL_FAIL;
 
61
        }
 
62
        
 
63
        *result = username;
 
64
        return SASL_OK;
 
65
}
 
66
 
 
67
static int gensec_sasl_get_realm(void *context, int id,
 
68
                                 const char **availrealms,
 
69
                                 const char **result)
 
70
{
 
71
        struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
 
72
        const char *realm = cli_credentials_get_realm(gensec_get_credentials(gensec_security));
 
73
        int i;
 
74
        if (id != SASL_CB_GETREALM) {
 
75
                return SASL_FAIL;
 
76
        }
 
77
 
 
78
        for (i=0; availrealms && availrealms[i]; i++) {
 
79
                if (strcasecmp_m(realm, availrealms[i]) == 0) {
 
80
                        result[i] = availrealms[i];
 
81
                        return SASL_OK;
 
82
                }
 
83
        }
 
84
        /* None of the realms match, so lets not specify one */
 
85
        *result = "";
 
86
        return SASL_OK;
 
87
}
 
88
 
 
89
static int gensec_sasl_get_password(sasl_conn_t *conn, void *context, int id,
 
90
                             sasl_secret_t **psecret)
 
91
{
 
92
        struct gensec_security *gensec_security = talloc_get_type(context, struct gensec_security);
 
93
        const char *password = cli_credentials_get_password(gensec_get_credentials(gensec_security));
 
94
        
 
95
        sasl_secret_t *secret;
 
96
        if (!password) {
 
97
                *psecret = NULL;
 
98
                return SASL_OK;
 
99
        }
 
100
        secret = talloc_size(gensec_security, sizeof(sasl_secret_t)+strlen(password));
 
101
        if (!secret) {
 
102
                return SASL_NOMEM;
 
103
        }
 
104
        secret->len = strlen(password);
 
105
        safe_strcpy((char*)secret->data, password, secret->len+1);
 
106
        *psecret = secret;
 
107
        return SASL_OK;
 
108
}
 
109
 
 
110
static int gensec_sasl_dispose(struct gensec_sasl_state *gensec_sasl_state)
 
111
{
 
112
        sasl_dispose(&gensec_sasl_state->conn);
 
113
        return SASL_OK;
 
114
}
 
115
 
 
116
static NTSTATUS gensec_sasl_client_start(struct gensec_security *gensec_security)
 
117
{
 
118
        struct gensec_sasl_state *gensec_sasl_state;
 
119
        const char *service = gensec_get_target_service(gensec_security);
 
120
        const char *target_name = gensec_get_target_hostname(gensec_security);
 
121
        struct socket_address *local_socket_addr = gensec_get_my_addr(gensec_security);
 
122
        struct socket_address *remote_socket_addr = gensec_get_peer_addr(gensec_security);
 
123
        char *local_addr = NULL;
 
124
        char *remote_addr = NULL;
 
125
        int sasl_ret;
 
126
 
 
127
        sasl_callback_t *callbacks;
 
128
 
 
129
        gensec_sasl_state = talloc(gensec_security, struct gensec_sasl_state);
 
130
        if (!gensec_sasl_state) {
 
131
                return NT_STATUS_NO_MEMORY;
 
132
        }
 
133
 
 
134
        callbacks = talloc_array(gensec_sasl_state, sasl_callback_t, 5);
 
135
        callbacks[0].id = SASL_CB_USER;
 
136
        callbacks[0].proc = gensec_sasl_get_user;
 
137
        callbacks[0].context = gensec_security;
 
138
 
 
139
        callbacks[1].id =  SASL_CB_AUTHNAME;
 
140
        callbacks[1].proc = gensec_sasl_get_user;
 
141
        callbacks[1].context = gensec_security;
 
142
 
 
143
        callbacks[2].id = SASL_CB_GETREALM;
 
144
        callbacks[2].proc = gensec_sasl_get_realm;
 
145
        callbacks[2].context = gensec_security;
 
146
 
 
147
        callbacks[3].id = SASL_CB_PASS;
 
148
        callbacks[3].proc = gensec_sasl_get_password;
 
149
        callbacks[3].context = gensec_security;
 
150
 
 
151
        callbacks[4].id = SASL_CB_LIST_END;
 
152
        callbacks[4].proc = NULL;
 
153
        callbacks[4].context = NULL;
 
154
 
 
155
        gensec_security->private_data = gensec_sasl_state;
 
156
 
 
157
        if (local_socket_addr) {
 
158
                local_addr = talloc_asprintf(gensec_sasl_state, 
 
159
                                             "%s;%d",
 
160
                                             local_socket_addr->addr, 
 
161
                                             local_socket_addr->port);
 
162
        }
 
163
 
 
164
        if (remote_socket_addr) {
 
165
                remote_addr = talloc_asprintf(gensec_sasl_state, 
 
166
                                             "%s;%d",
 
167
                                             remote_socket_addr->addr, 
 
168
                                             remote_socket_addr->port);
 
169
        }
 
170
        gensec_sasl_state->step = 0;
 
171
 
 
172
        sasl_ret = sasl_client_new(service,
 
173
                                   target_name,
 
174
                                   local_addr, remote_addr, callbacks, 0,
 
175
                                   &gensec_sasl_state->conn);
 
176
        
 
177
        if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
 
178
                sasl_security_properties_t props;
 
179
                talloc_set_destructor(gensec_sasl_state, gensec_sasl_dispose);
 
180
 
 
181
                ZERO_STRUCT(props);
 
182
                if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
 
183
                        props.min_ssf = 1;
 
184
                }
 
185
                if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
 
186
                        props.min_ssf = 40;
 
187
                }
 
188
                
 
189
                props.max_ssf = UINT_MAX;
 
190
                props.maxbufsize = 65536;
 
191
                sasl_ret = sasl_setprop(gensec_sasl_state->conn, SASL_SEC_PROPS, &props);
 
192
                if (sasl_ret != SASL_OK) {
 
193
                        return sasl_nt_status(sasl_ret);
 
194
                }
 
195
 
 
196
        } else {
 
197
                DEBUG(1, ("GENSEC SASL: client_new failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
 
198
        }
 
199
        return sasl_nt_status(sasl_ret);
 
200
}
 
201
 
 
202
static NTSTATUS gensec_sasl_update(struct gensec_security *gensec_security, 
 
203
                                   TALLOC_CTX *out_mem_ctx, 
 
204
                                   const DATA_BLOB in, DATA_BLOB *out) 
 
205
{
 
206
        struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
 
207
                                                                      struct gensec_sasl_state);
 
208
        int sasl_ret;
 
209
        const char *out_data;
 
210
        unsigned int out_len;
 
211
 
 
212
        if (gensec_sasl_state->step == 0) {
 
213
                const char *mech;
 
214
                sasl_ret = sasl_client_start(gensec_sasl_state->conn, gensec_security->ops->sasl_name, 
 
215
                                             NULL, &out_data, &out_len, &mech);
 
216
        } else {
 
217
                sasl_ret = sasl_client_step(gensec_sasl_state->conn,
 
218
                                            (char*)in.data, in.length, NULL,
 
219
                                            &out_data, &out_len);
 
220
        }
 
221
        if (sasl_ret == SASL_OK || sasl_ret == SASL_CONTINUE) {
 
222
                *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
 
223
        } else {
 
224
                DEBUG(1, ("GENSEC SASL: step %d update failed: %s\n", gensec_sasl_state->step, 
 
225
                          sasl_errdetail(gensec_sasl_state->conn)));
 
226
        }
 
227
        gensec_sasl_state->step++;
 
228
        return sasl_nt_status(sasl_ret);
 
229
}
 
230
 
 
231
static NTSTATUS gensec_sasl_unwrap_packets(struct gensec_security *gensec_security, 
 
232
                                        TALLOC_CTX *out_mem_ctx, 
 
233
                                        const DATA_BLOB *in, 
 
234
                                        DATA_BLOB *out,
 
235
                                        size_t *len_processed) 
 
236
{
 
237
        struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
 
238
                                                                      struct gensec_sasl_state);
 
239
        const char *out_data;
 
240
        unsigned int out_len;
 
241
 
 
242
        int sasl_ret = sasl_decode(gensec_sasl_state->conn,
 
243
                                   (char*)in->data, in->length, &out_data,
 
244
                                   &out_len);
 
245
        if (sasl_ret == SASL_OK) {
 
246
                *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
 
247
                *len_processed = in->length;
 
248
        } else {
 
249
                DEBUG(1, ("GENSEC SASL: unwrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
 
250
        }
 
251
        return sasl_nt_status(sasl_ret);
 
252
 
 
253
}
 
254
 
 
255
static NTSTATUS gensec_sasl_wrap_packets(struct gensec_security *gensec_security, 
 
256
                                        TALLOC_CTX *out_mem_ctx, 
 
257
                                        const DATA_BLOB *in, 
 
258
                                        DATA_BLOB *out,
 
259
                                        size_t *len_processed) 
 
260
{
 
261
        struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
 
262
                                                                      struct gensec_sasl_state);
 
263
        const char *out_data;
 
264
        unsigned int out_len;
 
265
 
 
266
        int sasl_ret = sasl_encode(gensec_sasl_state->conn,
 
267
                                   (char*)in->data, in->length, &out_data,
 
268
                                   &out_len);
 
269
        if (sasl_ret == SASL_OK) {
 
270
                *out = data_blob_talloc(out_mem_ctx, out_data, out_len);
 
271
                *len_processed = in->length;
 
272
        } else {
 
273
                DEBUG(1, ("GENSEC SASL: wrap failed: %s\n", sasl_errdetail(gensec_sasl_state->conn)));
 
274
        }
 
275
        return sasl_nt_status(sasl_ret);
 
276
}
 
277
 
 
278
/* Try to figure out what features we actually got on the connection */
 
279
static bool gensec_sasl_have_feature(struct gensec_security *gensec_security, 
 
280
                                     uint32_t feature) 
 
281
{
 
282
        struct gensec_sasl_state *gensec_sasl_state = talloc_get_type(gensec_security->private_data,
 
283
                                                                      struct gensec_sasl_state);
 
284
        sasl_ssf_t ssf;
 
285
        int sasl_ret = sasl_getprop(gensec_sasl_state->conn, SASL_SSF,
 
286
                        (const void**)&ssf);
 
287
        if (sasl_ret != SASL_OK) {
 
288
                return false;
 
289
        }
 
290
        if (feature & GENSEC_FEATURE_SIGN) {
 
291
                if (ssf == 0) {
 
292
                        return false;
 
293
                }
 
294
                if (ssf >= 1) {
 
295
                        return true;
 
296
                }
 
297
        }
 
298
        if (feature & GENSEC_FEATURE_SEAL) {
 
299
                if (ssf <= 1) {
 
300
                        return false;
 
301
                }
 
302
                if (ssf > 1) {
 
303
                        return true;
 
304
                }
 
305
        }
 
306
        return false;
 
307
}
 
308
 
 
309
/* This could in theory work with any SASL mech */
 
310
static const struct gensec_security_ops gensec_sasl_security_ops = {
 
311
        .name             = "sasl-DIGEST-MD5",
 
312
        .sasl_name        = "DIGEST-MD5",
 
313
        .client_start     = gensec_sasl_client_start,
 
314
        .update           = gensec_sasl_update,
 
315
        .wrap_packets     = gensec_sasl_wrap_packets,
 
316
        .unwrap_packets   = gensec_sasl_unwrap_packets,
 
317
        .have_feature     = gensec_sasl_have_feature,
 
318
        .enabled          = true,
 
319
        .priority         = GENSEC_SASL
 
320
};
 
321
 
 
322
static int gensec_sasl_log(void *context, 
 
323
                    int sasl_log_level,
 
324
                    const char *message) 
 
325
{
 
326
        int dl;
 
327
        switch (sasl_log_level) {
 
328
        case SASL_LOG_NONE:
 
329
                dl = 0;
 
330
                break;
 
331
        case SASL_LOG_ERR:
 
332
                dl = 1;
 
333
                break;
 
334
        case SASL_LOG_FAIL:
 
335
                dl = 2;
 
336
                break;
 
337
        case SASL_LOG_WARN:
 
338
                dl = 3;
 
339
                break;
 
340
        case SASL_LOG_NOTE:
 
341
                dl = 5;
 
342
                break;
 
343
        case SASL_LOG_DEBUG:
 
344
                dl = 10;
 
345
                break;
 
346
        case SASL_LOG_TRACE:
 
347
                dl = 11;
 
348
                break;
 
349
#if DEBUG_PASSWORD
 
350
        case SASL_LOG_PASS:
 
351
                dl = 100;
 
352
                break;
 
353
#endif
 
354
        default:
 
355
                dl = 0;
 
356
                break;
 
357
        }
 
358
        DEBUG(dl, ("gensec_sasl: %s\n", message));
 
359
 
 
360
        return SASL_OK;
 
361
}
 
362
 
 
363
NTSTATUS gensec_sasl_init(void)
 
364
{
 
365
        NTSTATUS ret;
 
366
        int sasl_ret;
 
367
#if 0
 
368
        int i;
 
369
        const char **sasl_mechs;
 
370
#endif
 
371
        
 
372
        static const sasl_callback_t callbacks[] = {
 
373
                { 
 
374
                        .id = SASL_CB_LOG,
 
375
                        .proc = gensec_sasl_log,
 
376
                        .context = NULL,
 
377
                },
 
378
                {
 
379
                        .id = SASL_CB_LIST_END,
 
380
                        .proc = gensec_sasl_log,
 
381
                        .context = NULL,
 
382
                }
 
383
        };
 
384
        sasl_ret = sasl_client_init(callbacks);
 
385
        
 
386
        if (sasl_ret == SASL_NOMECH) {
 
387
                /* Nothing to do here */
 
388
                return NT_STATUS_OK;
 
389
        }
 
390
 
 
391
        if (sasl_ret != SASL_OK) {
 
392
                return sasl_nt_status(sasl_ret);
 
393
        }
 
394
 
 
395
        /* For now, we just register DIGEST-MD5 */
 
396
#if 1
 
397
        ret = gensec_register(&gensec_sasl_security_ops);
 
398
        if (!NT_STATUS_IS_OK(ret)) {
 
399
                DEBUG(0,("Failed to register '%s' gensec backend!\n",
 
400
                         gensec_sasl_security_ops.name));
 
401
                return ret;
 
402
        }
 
403
#else
 
404
        sasl_mechs = sasl_global_listmech();
 
405
        for (i = 0; sasl_mechs && sasl_mechs[i]; i++) {
 
406
                const struct gensec_security_ops *oldmech;
 
407
                struct gensec_security_ops *newmech;
 
408
                oldmech = gensec_security_by_sasl_name(NULL, sasl_mechs[i]);
 
409
                if (oldmech) {
 
410
                        continue;
 
411
                }
 
412
                newmech = talloc(talloc_autofree_context(), struct gensec_security_ops);
 
413
                if (!newmech) {
 
414
                        return NT_STATUS_NO_MEMORY;
 
415
                }
 
416
                *newmech = gensec_sasl_security_ops;
 
417
                newmech->sasl_name = talloc_strdup(newmech, sasl_mechs[i]);
 
418
                newmech->name = talloc_asprintf(newmech, "sasl-%s", sasl_mechs[i]);
 
419
                if (!newmech->sasl_name || !newmech->name) {
 
420
                        return NT_STATUS_NO_MEMORY;
 
421
                }
 
422
 
 
423
                ret = gensec_register(newmech);
 
424
                if (!NT_STATUS_IS_OK(ret)) {
 
425
                        DEBUG(0,("Failed to register '%s' gensec backend!\n",
 
426
                                 gensec_sasl_security_ops.name));
 
427
                        return ret;
 
428
                }
 
429
        }
 
430
#endif
 
431
        return NT_STATUS_OK;
 
432
}