~ubuntu-branches/ubuntu/precise/autofs5/precise

« back to all changes in this revision

Viewing changes to .pc/autofs-5.0.5-fix-add-simple-bind-auth.patch/modules/cyrus-sasl.c

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-07-03 14:35:46 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110703143546-nej26krjij0rf792
Tags: 5.0.6-0ubuntu1
* New upstream release:
  - Dropped upstream patches 
  - Refreshed debian/patches/17ld.patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
   Copyright 2005 Red Hat, Inc.
3
 
   All rights reserved.
4
 
 
5
 
   Redistribution and use in source and binary forms, with or without
6
 
   modification, are permitted provided that the following conditions are met:
7
 
 
8
 
 
9
 
    * Redistributions of source code must retain the above copyright
10
 
      notice, this list of conditions and the following disclaimer.
11
 
    * Redistributions in binary form must reproduce the above copyright
12
 
      notice, this list of conditions and the following disclaimer in
13
 
      the documentation and/or other materials provided with the
14
 
      distribution.
15
 
    * Neither the name of Red Hat, Inc., nor the names of its
16
 
      contributors may be used to endorse or promote products derived
17
 
      from this software without specific prior written permission.
18
 
 
19
 
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20
 
   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21
 
   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
 
   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
23
 
   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
 
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
 
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
 
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
 
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
 
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
 
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 
 */
31
 
/*
32
 
 *  cyrus-sasl.c
33
 
 *
34
 
 *  Description:
35
 
 *
36
 
 *  This file implements SASL authentication to an LDAP server for the
37
 
 *  following mechanisms:
38
 
 *    GSSAPI, EXTERNAL, ANONYMOUS, PLAIN, DIGEST-MD5, KERBEROS_V5, LOGIN
39
 
 *  The mechanism to use is specified in an external file,
40
 
 *  LDAP_AUTH_CONF_FILE.  See the samples directory in the autofs
41
 
 *  distribution for an example configuration file.
42
 
 *
43
 
 *  This file is written with the intent that it will work with both the
44
 
 *  openldap and the netscape ldap client libraries.
45
 
 *
46
 
 *  Author: Nalin Dahyabhai <nalin@redhat.com>
47
 
 *  Modified by Jeff Moyer <jmoyer@redhat.com> to adapt it to autofs.
48
 
 */
49
 
#include <sys/types.h>
50
 
#include <sys/wait.h>
51
 
#include <stdio.h>
52
 
#include <stdlib.h>
53
 
#include <string.h>
54
 
#include <ldap.h>
55
 
#include <sasl/sasl.h>
56
 
 
57
 
#include "automount.h"
58
 
#include "lookup_ldap.h"
59
 
 
60
 
#ifndef LDAP_OPT_RESULT_CODE
61
 
#ifdef  LDAP_OPT_ERROR_NUMBER
62
 
#define LDAP_OPT_RESULT_CODE LDAP_OPT_ERROR_NUMBER
63
 
#else
64
 
#error "Could not determine the proper value for LDAP_OPT_RESULT_CODE."
65
 
#endif
66
 
#endif
67
 
 
68
 
/*
69
 
 *  Once a krb5 credentials cache is setup, we need to set the KRB5CCNAME
70
 
 *  environment variable so that the library knows where to find it.
71
 
 */
72
 
static const char *krb5ccenv = "KRB5CCNAME";
73
 
static const char *krb5ccval = "MEMORY:_autofstkt";
74
 
static const char *default_client = "autofsclient";
75
 
static pthread_mutex_t krb5cc_mutex = PTHREAD_MUTEX_INITIALIZER;
76
 
static unsigned int krb5cc_in_use = 0;
77
 
 
78
 
static int sasl_log_func(void *, int, const char *);
79
 
static int getpass_func(sasl_conn_t *, void *, int, sasl_secret_t **);
80
 
static int getuser_func(void *, int, const char **, unsigned *);
81
 
 
82
 
static sasl_callback_t callbacks[] = {
83
 
        { SASL_CB_LOG, &sasl_log_func, NULL },
84
 
        { SASL_CB_USER, &getuser_func, NULL },
85
 
        { SASL_CB_AUTHNAME, &getuser_func, NULL },
86
 
        { SASL_CB_PASS, &getpass_func, NULL },
87
 
        { SASL_CB_LIST_END, NULL, NULL },
88
 
};
89
 
 
90
 
static char *sasl_auth_id = NULL;
91
 
static char *sasl_auth_secret = NULL;
92
 
 
93
 
static int
94
 
sasl_log_func(void *context, int level, const char *message)
95
 
{
96
 
        switch (level) {
97
 
        case SASL_LOG_ERR:
98
 
        case SASL_LOG_FAIL:
99
 
                logerr("%s", message);
100
 
                break;
101
 
        case SASL_LOG_WARN:
102
 
                logmsg("%s", message);
103
 
                break;
104
 
        case SASL_LOG_NOTE:
105
 
                logmsg("%s", message);
106
 
                break;
107
 
        case SASL_LOG_DEBUG:
108
 
        case SASL_LOG_TRACE:
109
 
        case SASL_LOG_PASS:
110
 
                debug(LOGOPT_DEBUG, "%s", message);
111
 
                break;
112
 
        default:
113
 
                break;
114
 
        }
115
 
 
116
 
        return SASL_OK;
117
 
}
118
 
 
119
 
static int
120
 
getuser_func(void *context, int id, const char **result, unsigned *len)
121
 
{
122
 
        debug(LOGOPT_NONE, "called with context %p, id %d.", context, id);
123
 
 
124
 
        switch (id) {
125
 
        case SASL_CB_USER:
126
 
        case SASL_CB_AUTHNAME:
127
 
                *result = sasl_auth_id;
128
 
                if (len)
129
 
                        *len = strlen(sasl_auth_id);
130
 
                break;
131
 
        default:
132
 
                error(LOGOPT_VERBOSE, "unknown id in request: %d", id);
133
 
                return SASL_FAIL;
134
 
        }
135
 
 
136
 
        return SASL_OK;
137
 
}
138
 
 
139
 
/*
140
 
 *  This function creates a sasl_secret_t from the credentials specified in
141
 
 *  the configuration file.  sasl_client_auth can return SASL_OK or
142
 
 *  SASL_NOMEM.  We simply propagate this return value to the caller.
143
 
 */
144
 
static int
145
 
getpass_func(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
146
 
{
147
 
        int len = strlen(sasl_auth_secret);
148
 
 
149
 
        debug(LOGOPT_NONE, "context %p, id %d", context, id);
150
 
 
151
 
        *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
152
 
        if (!*psecret)
153
 
                return SASL_NOMEM;
154
 
 
155
 
        (*psecret)->len = strlen(sasl_auth_secret);
156
 
        strncpy((char *)(*psecret)->data, sasl_auth_secret, len);
157
 
 
158
 
        return SASL_OK;
159
 
}
160
 
 
161
 
/*
162
 
 *  retrieves the supportedSASLmechanisms from the LDAP server.
163
 
 *
164
 
 *  Return Value: the result of ldap_get_values on success, NULL on failure.
165
 
 *                The caller is responsible for calling ldap_value_free on
166
 
 *                the returned data.
167
 
 */
168
 
char **
169
 
get_server_SASL_mechanisms(unsigned logopt, LDAP *ld)
170
 
{
171
 
        int ret;
172
 
        const char *saslattrlist[] = {"supportedSASLmechanisms", NULL};
173
 
        LDAPMessage *results = NULL, *entry;
174
 
        char **mechanisms;
175
 
 
176
 
        ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
177
 
                                (char **)saslattrlist, 0,
178
 
                                NULL, NULL,
179
 
                                NULL, LDAP_NO_LIMIT, &results);
180
 
        if (ret != LDAP_SUCCESS) {
181
 
                error(logopt, "%s", ldap_err2string(ret));
182
 
                return NULL;
183
 
        }
184
 
 
185
 
        entry = ldap_first_entry(ld, results);
186
 
        if (entry == NULL) {
187
 
                /* No root DSE. (!) */
188
 
                ldap_msgfree(results);
189
 
                debug(logopt,
190
 
                      "a lookup of \"supportedSASLmechanisms\" returned "
191
 
                      "no results.");
192
 
                return NULL;
193
 
        }
194
 
 
195
 
        mechanisms = ldap_get_values(ld, entry, "supportedSASLmechanisms");
196
 
        ldap_msgfree(results);
197
 
        if (mechanisms == NULL) {
198
 
                /* Well, that was a waste of time. */
199
 
                info(logopt, "No SASL authentication mechanisms are supported"
200
 
                    " by the LDAP server.");
201
 
                return NULL;
202
 
        }
203
 
 
204
 
        return mechanisms;
205
 
}
206
 
 
207
 
/*
208
 
 *  Returns 0 upon successful connect, -1 on failure.
209
 
 */
210
 
int
211
 
do_sasl_bind(unsigned logopt, LDAP *ld, sasl_conn_t *conn, const char **clientout,
212
 
             unsigned int *clientoutlen, const char *auth_mech, int sasl_result)
213
 
{
214
 
        int ret, msgid, bind_result;
215
 
        struct berval client_cred, *server_cred, temp_cred;
216
 
        LDAPMessage *results;
217
 
        int have_data, expected_data;
218
 
 
219
 
        do {
220
 
                /* Take whatever client data we have and send it to the
221
 
                 * server. */
222
 
                client_cred.bv_val = (char *)*clientout;
223
 
                client_cred.bv_len = *clientoutlen;
224
 
                ret = ldap_sasl_bind(ld, NULL, auth_mech,
225
 
                                     (client_cred.bv_len > 0) ?
226
 
                                     &client_cred : NULL,
227
 
                                     NULL, NULL, &msgid);
228
 
                if (ret != LDAP_SUCCESS) {
229
 
                        crit(logopt,
230
 
                             "Error sending sasl_bind request to "
231
 
                             "the server: %s", ldap_err2string(ret));
232
 
                        return -1;
233
 
                }
234
 
 
235
 
                /* Wait for a result message for this bind request. */
236
 
                results = NULL;
237
 
                ret = ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &results);
238
 
                if (ret != LDAP_RES_BIND) {
239
 
                        crit(logopt,
240
 
                             "Error while waiting for response to "
241
 
                             "sasl_bind request: %s", ldap_err2string(ret));
242
 
                        return -1;
243
 
                }
244
 
 
245
 
                /* Retrieve the result code for the bind request and
246
 
                 * any data which the server sent. */
247
 
                server_cred = NULL;
248
 
                ret = ldap_parse_sasl_bind_result(ld, results,
249
 
                                                  &server_cred, 0);
250
 
                ldap_msgfree(results);
251
 
 
252
 
                /* Okay, here's where things get tricky.  Both
253
 
                 * Mozilla's LDAP SDK and OpenLDAP store the result
254
 
                 * code which was returned by the server in the
255
 
                 * handle's ERROR_NUMBER option.  Mozilla returns
256
 
                 * LDAP_SUCCESS if the data was parsed correctly, even
257
 
                 * if the result was an error, while OpenLDAP returns
258
 
                 * the result code.  I'm leaning toward Mozilla being
259
 
                 * more correct.
260
 
                 * In either case, we stuff the result into bind_result.
261
 
                 */
262
 
                if (ret == LDAP_SUCCESS) {
263
 
                        /* Mozilla? */
264
 
                        ret = ldap_get_option(ld, LDAP_OPT_RESULT_CODE,
265
 
                                              &bind_result);
266
 
                        if (ret != LDAP_SUCCESS) {
267
 
                                crit(logopt,
268
 
                                     "Error retrieving response to sasl_bind "
269
 
                                     "request: %s", ldap_err2string(ret));
270
 
                                ret = -1;
271
 
                                break;
272
 
                        }
273
 
                } else {
274
 
                        /* OpenLDAP? */
275
 
                        switch (ret) {
276
 
                        case LDAP_SASL_BIND_IN_PROGRESS:
277
 
                                bind_result = ret;
278
 
                                break;
279
 
                        default:
280
 
                                warn(logopt,
281
 
                                     "Error parsing response to sasl_bind "
282
 
                                     "request: %s.", ldap_err2string(ret));
283
 
                                break;
284
 
                        }
285
 
                }
286
 
 
287
 
                /*
288
 
                 * The LDAP server is supposed to send a NULL value for
289
 
                 * server_cred if there was no data.  However, *some*
290
 
                 * server implementations get this wrong, and instead send
291
 
                 * an empty string.  We check for both.
292
 
                 */
293
 
                have_data = server_cred != NULL && server_cred->bv_len > 0;
294
 
 
295
 
                /*
296
 
                 * If the result of the sasl_client_start is SASL_CONTINUE,
297
 
                 * then the server should have sent us more data.
298
 
                 */
299
 
                expected_data = sasl_result == SASL_CONTINUE;
300
 
 
301
 
                if (have_data && !expected_data) {
302
 
                        warn(logopt,
303
 
                             "The LDAP server sent data in response to our "
304
 
                             "bind request, but indicated that the bind was "
305
 
                             "complete. LDAP SASL bind with mechansim %s "
306
 
                             "failed.", auth_mech);
307
 
                        ret = -1;
308
 
                        break;
309
 
                }
310
 
                if (expected_data && !have_data) {
311
 
                        warn(logopt,
312
 
                             "The LDAP server indicated that the LDAP SASL "
313
 
                             "bind was incomplete, but did not provide the "
314
 
                             "required data to proceed. LDAP SASL bind with "
315
 
                             "mechanism %s failed.", auth_mech);
316
 
                        ret = -1;
317
 
                        break;
318
 
                }
319
 
 
320
 
                /* If we need another round trip, process whatever we
321
 
                 * received and prepare data to be transmitted back. */
322
 
                if ((sasl_result == SASL_CONTINUE) &&
323
 
                    ((bind_result == LDAP_SUCCESS) ||
324
 
                     (bind_result == LDAP_SASL_BIND_IN_PROGRESS))) {
325
 
                        if (server_cred != NULL) {
326
 
                                temp_cred = *server_cred;
327
 
                        } else {
328
 
                                temp_cred.bv_len = 0;
329
 
                                temp_cred.bv_val = NULL;
330
 
                        }
331
 
                        sasl_result = sasl_client_step(conn,
332
 
                                                       temp_cred.bv_val,
333
 
                                                       temp_cred.bv_len,
334
 
                                                       NULL,
335
 
                                                       clientout,
336
 
                                                       clientoutlen);
337
 
                        /* If we have data to send, then the server
338
 
                         * had better be expecting it.  (It's valid
339
 
                         * to send the server no data with a request.)
340
 
                         */
341
 
                        if ((*clientoutlen > 0) &&
342
 
                            (bind_result != LDAP_SASL_BIND_IN_PROGRESS)) {
343
 
                                warn(logopt,
344
 
                                     "We have data for the server, "
345
 
                                     "but it thinks we are done!");
346
 
                                /* XXX should print out debug data here */
347
 
                                ret = -1;
348
 
                        }
349
 
                }
350
 
 
351
 
                if (server_cred && server_cred->bv_len > 0) {
352
 
                        ber_bvfree(server_cred);
353
 
                        server_cred = NULL;
354
 
                }
355
 
 
356
 
        } while ((bind_result == LDAP_SASL_BIND_IN_PROGRESS) ||
357
 
                 (sasl_result == SASL_CONTINUE));
358
 
 
359
 
        if (server_cred && server_cred->bv_len > 0)
360
 
                ber_bvfree(server_cred);
361
 
 
362
 
        return ret;
363
 
}
364
 
 
365
 
/*
366
 
 *  Read client credentials from the default keytab, create a credentials
367
 
 *  cache, add the TGT to that cache, and set the environment variable so
368
 
 *  that the sasl/krb5 libraries can find our credentials.
369
 
 *
370
 
 *  Returns 0 upon success.  ctxt->kinit_done and ctxt->kinit_successful
371
 
 *  are set for cleanup purposes.  The krb5 context and ccache entries in
372
 
 *  the lookup_context are also filled in.
373
 
 *
374
 
 *  Upon failure, -1 is returned.
375
 
 */
376
 
int
377
 
sasl_do_kinit(unsigned logopt, struct lookup_context *ctxt)
378
 
{
379
 
        krb5_error_code ret;
380
 
        krb5_principal tgs_princ, krb5_client_princ;
381
 
        krb5_creds my_creds;
382
 
        char *tgs_name;
383
 
        int status;
384
 
 
385
 
        if (ctxt->kinit_done)
386
 
                return 0;
387
 
        ctxt->kinit_done = 1;
388
 
 
389
 
        debug(logopt,
390
 
              "initializing kerberos ticket: client principal %s",
391
 
              ctxt->client_princ ? ctxt->client_princ : default_client);
392
 
 
393
 
        ret = krb5_init_context(&ctxt->krb5ctxt);
394
 
        if (ret) {
395
 
                error(logopt, "krb5_init_context failed with %d", ret);
396
 
                return -1;
397
 
        }
398
 
 
399
 
        ret = krb5_cc_resolve(ctxt->krb5ctxt, krb5ccval, &ctxt->krb5_ccache);
400
 
        if (ret) {
401
 
                error(logopt, "krb5_cc_resolve failed with error %d",
402
 
                      ret);
403
 
                krb5_free_context(ctxt->krb5ctxt);
404
 
                return -1;
405
 
        }
406
 
 
407
 
        if (ctxt->client_princ) {
408
 
                debug(logopt,
409
 
                      "calling krb5_parse_name on client principal %s",
410
 
                      ctxt->client_princ);
411
 
 
412
 
                ret = krb5_parse_name(ctxt->krb5ctxt, ctxt->client_princ,
413
 
                                      &krb5_client_princ);
414
 
                if (ret) {
415
 
                        error(logopt,
416
 
                              "krb5_parse_name failed for "
417
 
                              "specified client principal %s",
418
 
                              ctxt->client_princ);
419
 
                        goto out_cleanup_cc;
420
 
                }
421
 
        } else {
422
 
                char *tmp_name = NULL;
423
 
 
424
 
                debug(logopt,
425
 
                      "calling krb5_sname_to_principal using defaults");
426
 
 
427
 
                ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL,
428
 
                                        default_client, KRB5_NT_SRV_HST, 
429
 
                                        &krb5_client_princ);
430
 
                if (ret) {
431
 
                        error(logopt,
432
 
                              "krb5_sname_to_principal failed for "
433
 
                              "%s with error %d", default_client, ret);
434
 
                        goto out_cleanup_cc;
435
 
                }
436
 
 
437
 
 
438
 
                ret = krb5_unparse_name(ctxt->krb5ctxt,
439
 
                                        krb5_client_princ, &tmp_name);
440
 
                if (ret) {
441
 
                        debug(logopt,
442
 
                              "krb5_unparse_name failed with error %d",
443
 
                              ret);
444
 
                        goto out_cleanup_client_princ;
445
 
                }
446
 
 
447
 
                debug(logopt,
448
 
                      "principal used for authentication: %s", tmp_name);
449
 
 
450
 
                krb5_free_unparsed_name(ctxt->krb5ctxt, tmp_name);
451
 
        }
452
 
 
453
 
        /* setup a principal for the ticket granting service */
454
 
        ret = krb5_build_principal_ext(ctxt->krb5ctxt, &tgs_princ,
455
 
                krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)->length,
456
 
                krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)->data,
457
 
                strlen(KRB5_TGS_NAME), KRB5_TGS_NAME,
458
 
                krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)->length,
459
 
                krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)->data,
460
 
                0);
461
 
        if (ret) {
462
 
                error(logopt,
463
 
                      "krb5_build_principal failed with error %d", ret);
464
 
                goto out_cleanup_client_princ;
465
 
        }
466
 
 
467
 
        ret = krb5_unparse_name(ctxt->krb5ctxt, tgs_princ, &tgs_name);
468
 
        if (ret) {
469
 
                error(logopt, "krb5_unparse_name failed with error %d",
470
 
                      ret);
471
 
                goto out_cleanup_client_princ;
472
 
        }
473
 
 
474
 
        debug(logopt, "Using tgs name %s", tgs_name);
475
 
 
476
 
        memset(&my_creds, 0, sizeof(my_creds));
477
 
        ret = krb5_get_init_creds_keytab(ctxt->krb5ctxt, &my_creds,
478
 
                                         krb5_client_princ,
479
 
                                         NULL /*keytab*/,
480
 
                                         0 /* relative start time */,
481
 
                                         tgs_name, NULL);
482
 
        if (ret) {
483
 
                error(logopt,
484
 
                      "krb5_get_init_creds_keytab failed with error %d",
485
 
                      ret);
486
 
                goto out_cleanup_unparse;
487
 
        }
488
 
 
489
 
        status = pthread_mutex_lock(&krb5cc_mutex);
490
 
        if (status)
491
 
                fatal(status);
492
 
 
493
 
        if (krb5cc_in_use++ == 0)
494
 
                /* tell the cache what the default principal is */
495
 
                ret = krb5_cc_initialize(ctxt->krb5ctxt,
496
 
                                 ctxt->krb5_ccache, krb5_client_princ);
497
 
 
498
 
        status = pthread_mutex_unlock(&krb5cc_mutex);
499
 
        if (status)
500
 
                fatal(status);
501
 
 
502
 
        if (ret) {
503
 
                error(logopt,
504
 
                      "krb5_cc_initialize failed with error %d", ret);
505
 
                goto out_cleanup_creds;
506
 
        }
507
 
 
508
 
        /* and store credentials for that principal */
509
 
        ret = krb5_cc_store_cred(ctxt->krb5ctxt, ctxt->krb5_ccache, &my_creds);
510
 
        if (ret) {
511
 
                error(logopt,
512
 
                      "krb5_cc_store_cred failed with error %d", ret);
513
 
                goto out_cleanup_creds;
514
 
        }
515
 
 
516
 
        /* finally, set the environment variable to point to our
517
 
         * credentials cache */
518
 
        if (setenv(krb5ccenv, krb5ccval, 1) != 0) {
519
 
                error(logopt, "setenv failed with %d", errno);
520
 
                goto out_cleanup_creds;
521
 
        }
522
 
        ctxt->kinit_successful = 1;
523
 
 
524
 
        debug(logopt, "Kerberos authentication was successful!");
525
 
 
526
 
        krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name);
527
 
        krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds);
528
 
        krb5_free_principal(ctxt->krb5ctxt, tgs_princ);
529
 
        krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
530
 
 
531
 
        return 0;
532
 
 
533
 
out_cleanup_creds:
534
 
        krb5cc_in_use--;
535
 
        krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds);
536
 
out_cleanup_unparse:
537
 
        krb5_free_principal(ctxt->krb5ctxt, tgs_princ);
538
 
        krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name);
539
 
out_cleanup_client_princ:
540
 
        krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
541
 
out_cleanup_cc:
542
 
        status = pthread_mutex_lock(&krb5cc_mutex);
543
 
        if (status)
544
 
                fatal(status);
545
 
 
546
 
        if (krb5cc_in_use)
547
 
                ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
548
 
        else
549
 
                ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache);
550
 
        if (ret)
551
 
                warn(logopt,
552
 
                     "krb5_cc_destroy failed with non-fatal error %d", ret);
553
 
 
554
 
        status = pthread_mutex_unlock(&krb5cc_mutex);
555
 
        if (status)
556
 
                fatal(status);
557
 
 
558
 
        krb5_free_context(ctxt->krb5ctxt);
559
 
 
560
 
        return -1;
561
 
}
562
 
 
563
 
/*
564
 
 *  Check a client given external credential cache.
565
 
 *
566
 
 *  Returns 0 upon success.  ctxt->kinit_done and ctxt->kinit_successful
567
 
 *  are set for cleanup purposes.  The krb5 context and ccache entries in
568
 
 *  the lookup_context are also filled in.
569
 
 *
570
 
 *  Upon failure, -1 is returned.
571
 
 */
572
 
int
573
 
sasl_do_kinit_ext_cc(unsigned logopt, struct lookup_context *ctxt)
574
 
{
575
 
        krb5_principal def_princ;
576
 
        krb5_principal krb5_client_princ;
577
 
        krb5_error_code ret;
578
 
        char *cc_princ, *client_princ;
579
 
 
580
 
        if (ctxt->kinit_done)
581
 
                return 0;
582
 
        ctxt->kinit_done = 1;
583
 
 
584
 
        debug(logopt,
585
 
              "using external credential cache for auth: client principal %s",
586
 
              ctxt->client_princ ? ctxt->client_princ : default_client);
587
 
 
588
 
        ret = krb5_init_context(&ctxt->krb5ctxt);
589
 
        if (ret) {
590
 
                error(logopt, "krb5_init_context failed with %d", ret);
591
 
                return -1;
592
 
        }
593
 
 
594
 
        ret = krb5_cc_resolve(ctxt->krb5ctxt, ctxt->client_cc, &ctxt->krb5_ccache);
595
 
        if (ret) {
596
 
                error(logopt, "krb5_cc_resolve failed with error %d",
597
 
                      ret);
598
 
                krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
599
 
                krb5_free_context(ctxt->krb5ctxt);
600
 
                return -1;
601
 
        }
602
 
 
603
 
        ret = krb5_cc_get_principal(ctxt->krb5ctxt, ctxt->krb5_ccache, &def_princ);
604
 
        if (ret) {
605
 
                error(logopt, "krb5_cc_get_principal failed with error %d", ret);
606
 
                krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
607
 
                krb5_free_context(ctxt->krb5ctxt);
608
 
                return -1;
609
 
        }
610
 
 
611
 
        ret = krb5_unparse_name(ctxt->krb5ctxt, def_princ, &cc_princ);
612
 
        if (ret) {
613
 
                error(logopt, "krb5_unparse_name failed with error %d", ret);
614
 
                krb5_free_principal(ctxt->krb5ctxt, def_princ);
615
 
                krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
616
 
                krb5_free_context(ctxt->krb5ctxt);
617
 
                return -1;
618
 
        }
619
 
 
620
 
        debug(logopt, "external credential cache default principal %s", cc_princ);
621
 
 
622
 
        /*
623
 
         * If the principal isn't set in the config construct the default
624
 
         * so we can check against the default principal of the external
625
 
         * cred cache.
626
 
         */
627
 
        if (ctxt->client_princ)
628
 
                client_princ = ctxt->client_princ;
629
 
        else {
630
 
                debug(logopt,
631
 
                      "calling krb5_sname_to_principal using defaults");
632
 
 
633
 
                ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL,
634
 
                                        default_client, KRB5_NT_SRV_HST, 
635
 
                                        &krb5_client_princ);
636
 
                if (ret) {
637
 
                        error(logopt,
638
 
                              "krb5_sname_to_principal failed for "
639
 
                              "%s with error %d", default_client, ret);
640
 
                        krb5_free_principal(ctxt->krb5ctxt, def_princ);
641
 
                        krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
642
 
                        krb5_free_context(ctxt->krb5ctxt);
643
 
                        return -1;
644
 
                }
645
 
 
646
 
 
647
 
                ret = krb5_unparse_name(ctxt->krb5ctxt,
648
 
                                        krb5_client_princ, &client_princ);
649
 
                if (ret) {
650
 
                        debug(logopt,
651
 
                              "krb5_unparse_name failed with error %d",
652
 
                              ret);
653
 
                        krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
654
 
                        krb5_free_principal(ctxt->krb5ctxt, def_princ);
655
 
                        krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
656
 
                        krb5_free_context(ctxt->krb5ctxt);
657
 
                        return -1;
658
 
                }
659
 
 
660
 
                debug(logopt,
661
 
                      "principal used for authentication: %s", client_princ);
662
 
 
663
 
                krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
664
 
        }
665
 
 
666
 
        /*
667
 
         * Check if the principal to be used matches the default principal in
668
 
         * the external cred cache.
669
 
         */
670
 
        if (strcmp(cc_princ, client_princ)) {
671
 
                error(logopt,
672
 
                      "configured client principal %s ",
673
 
                      ctxt->client_princ);
674
 
                error(logopt,
675
 
                      "external credential cache default principal %s",
676
 
                      cc_princ);
677
 
                error(logopt, 
678
 
                      "cannot use credential cache, external "
679
 
                      "default principal does not match configured "
680
 
                      "principal");
681
 
                if (!ctxt->client_princ)
682
 
                        krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ);
683
 
                krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ);
684
 
                krb5_free_principal(ctxt->krb5ctxt, def_princ);
685
 
                krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
686
 
                krb5_free_context(ctxt->krb5ctxt);
687
 
                return -1;
688
 
        }
689
 
 
690
 
        if (!ctxt->client_princ)
691
 
                krb5_free_unparsed_name(ctxt->krb5ctxt, client_princ);
692
 
        krb5_free_unparsed_name(ctxt->krb5ctxt, cc_princ);
693
 
        krb5_free_principal(ctxt->krb5ctxt, def_princ);
694
 
 
695
 
        /* Set the environment variable to point to the external cred cache */
696
 
        if (setenv(krb5ccenv, ctxt->client_cc, 1) != 0) {
697
 
                error(logopt, "setenv failed with %d", errno);
698
 
                krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
699
 
                krb5_free_context(ctxt->krb5ctxt);
700
 
                return -1;
701
 
        }
702
 
        ctxt->kinit_successful = 1;
703
 
 
704
 
        debug(logopt, "Kerberos authentication was successful!");
705
 
 
706
 
        return 0;
707
 
}
708
 
 
709
 
/*
710
 
 *  Attempt to bind to the ldap server using a given authentication
711
 
 *  mechanism.  ldap should be a properly initialzed ldap pointer.
712
 
 *
713
 
 *  Returns a valid sasl_conn_t pointer upon success, NULL on failure.
714
 
 */
715
 
sasl_conn_t *
716
 
sasl_bind_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt, const char *mech)
717
 
{
718
 
        sasl_conn_t *conn;
719
 
        char *tmp, *host = NULL;
720
 
        const char *clientout;
721
 
        unsigned int clientoutlen;
722
 
        const char *chosen_mech;
723
 
        int result;
724
 
 
725
 
        if (!strncmp(mech, "GSSAPI", 6)) {
726
 
                if (ctxt->client_cc)
727
 
                        result = sasl_do_kinit_ext_cc(logopt, ctxt);
728
 
                else
729
 
                        result = sasl_do_kinit(logopt, ctxt);
730
 
                if (result != 0)
731
 
                        return NULL;
732
 
        }
733
 
 
734
 
        debug(logopt, "Attempting sasl bind with mechanism %s", mech);
735
 
 
736
 
        result = ldap_get_option(ldap, LDAP_OPT_HOST_NAME, &host);
737
 
        if (result != LDAP_OPT_SUCCESS || !host) {
738
 
                debug(logopt, "failed to get hostname for connection");
739
 
                return NULL;
740
 
        }
741
 
 
742
 
        if ((tmp = strrchr(host, ':'))) {
743
 
                if (*(tmp - 1) != ']') {
744
 
                        *tmp = '\0';
745
 
                        tmp = host;
746
 
                } else {
747
 
                        *(tmp - 1) = '\0';
748
 
                        tmp = host;
749
 
                        if (*tmp == '[')
750
 
                                tmp++;
751
 
                }
752
 
        }
753
 
 
754
 
        /* Create a new authentication context for the service. */
755
 
        result = sasl_client_new("ldap", tmp, NULL, NULL, NULL, 0, &conn);
756
 
        if (result != SASL_OK) {
757
 
                error(logopt, "sasl_client_new failed with error %d",
758
 
                      result);
759
 
                ldap_memfree(host);
760
 
                return NULL;
761
 
        }
762
 
 
763
 
        chosen_mech = NULL;
764
 
        result = sasl_client_start(conn, mech, NULL,
765
 
                                &clientout, &clientoutlen, &chosen_mech);
766
 
 
767
 
        /* OK and CONTINUE are the only non-fatal return codes here. */
768
 
        if ((result != SASL_OK) && (result != SASL_CONTINUE)) {
769
 
                warn(logopt, "sasl_client_start failed for %s", host);
770
 
                debug(logopt, "sasl_client_start: %s", sasl_errdetail(conn));
771
 
                ldap_memfree(host);
772
 
                sasl_dispose(&conn);
773
 
                return NULL;
774
 
        }
775
 
 
776
 
        result = do_sasl_bind(logopt, ldap, conn,
777
 
                         &clientout, &clientoutlen, chosen_mech, result);
778
 
        if (result == 0) {
779
 
                ldap_memfree(host);
780
 
                debug(logopt, "sasl bind with mechanism %s succeeded",
781
 
                      chosen_mech);
782
 
                return conn;
783
 
        }
784
 
 
785
 
        info(logopt, "sasl bind with mechanism %s failed", mech);
786
 
 
787
 
        /* sasl bind failed */
788
 
        ldap_memfree(host);
789
 
        sasl_dispose(&conn);
790
 
 
791
 
        return NULL;
792
 
}
793
 
 
794
 
/*
795
 
 *  Returns 0 if a suitable authentication mechanism is available.  Returns
796
 
 *  -1 on error or if no mechanism is supported by both client and server.
797
 
 */
798
 
sasl_conn_t *
799
 
sasl_choose_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
800
 
{
801
 
        sasl_conn_t *conn = NULL;
802
 
        int authenticated;
803
 
        int i;
804
 
        char **mechanisms;
805
 
 
806
 
        mechanisms = get_server_SASL_mechanisms(logopt, ldap);
807
 
        if (!mechanisms)
808
 
                return NULL;
809
 
 
810
 
        /* Try each supported mechanism in turn. */
811
 
        authenticated = 0;
812
 
        for (i = 0; mechanisms[i] != NULL; i++) {
813
 
                /*
814
 
                 *  This routine is called if there is no configured
815
 
                 *  mechanism.  As such, we can skip over any auth
816
 
                 *  mechanisms that require user credentials.  These include
817
 
                 *  PLAIN, LOGIN, and DIGEST-MD5.
818
 
                 */
819
 
                if (authtype_requires_creds(mechanisms[i]))
820
 
                        continue;
821
 
 
822
 
                conn = sasl_bind_mech(logopt, ldap, ctxt, mechanisms[i]);
823
 
                if (conn) {
824
 
                        ctxt->sasl_mech = strdup(mechanisms[i]);
825
 
                        if (!ctxt->sasl_mech) {
826
 
                                crit(logopt, "Successfully authenticated with "
827
 
                                     "mechanism %s, but failed to allocate "
828
 
                                     "memory to hold the mechanism type.",
829
 
                                     mechanisms[i]);
830
 
                                sasl_dispose(&conn);
831
 
                                ldap_value_free(mechanisms);
832
 
                                return NULL;
833
 
                        }
834
 
                        authenticated = 1;
835
 
                        break;
836
 
                }
837
 
                debug(logopt, "Failed to authenticate with mech %s",
838
 
                      mechanisms[i]);
839
 
        }
840
 
 
841
 
        debug(logopt, "authenticated: %d, sasl_mech: %s",
842
 
              authenticated, ctxt->sasl_mech);
843
 
 
844
 
        ldap_value_free(mechanisms);
845
 
        return conn;
846
 
}
847
 
 
848
 
/*
849
 
 *  Routine called when unbinding an ldap connection.
850
 
 */
851
 
void
852
 
autofs_sasl_unbind(struct lookup_context *ctxt)
853
 
{
854
 
        if (ctxt->sasl_conn) {
855
 
                sasl_dispose(&ctxt->sasl_conn);
856
 
                ctxt->sasl_conn = NULL;
857
 
        }
858
 
}
859
 
 
860
 
/*
861
 
 *  Given a lookup context that has been initialized with any user-specified
862
 
 *  parameters, figure out which sasl mechanism to use.  Then, initialize
863
 
 *  the necessary parameters to authenticate with the chosen mechanism.
864
 
 *
865
 
 *  Return Values:
866
 
 *  0  -  Success
867
 
 * -1  -  Failure
868
 
 */
869
 
int
870
 
autofs_sasl_bind(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
871
 
{
872
 
        sasl_conn_t *conn = NULL;
873
 
 
874
 
        /* If we already have a connection use it */
875
 
        if (ctxt->sasl_conn)
876
 
                return 0;
877
 
 
878
 
        if (ctxt->sasl_mech && !strncmp(ctxt->sasl_mech, "EXTERNAL", 8)) {
879
 
                int result;
880
 
 
881
 
                debug(logopt,
882
 
                      "Attempting sasl bind with mechanism %s",
883
 
                      ctxt->sasl_mech);
884
 
 
885
 
                result = do_sasl_extern(ldap, ctxt);
886
 
                if (result)
887
 
                        debug(logopt,
888
 
                              "Failed to authenticate with mech %s",
889
 
                              ctxt->sasl_mech);
890
 
                else
891
 
                        debug(logopt,
892
 
                              "sasl bind with mechanism %s succeeded",
893
 
                              ctxt->sasl_mech);
894
 
 
895
 
                return result;
896
 
        }
897
 
 
898
 
        sasl_auth_id = ctxt->user;
899
 
        sasl_auth_secret = ctxt->secret;
900
 
 
901
 
        if (ctxt->auth_required & LDAP_AUTH_AUTODETECT) {
902
 
                if (ctxt->sasl_mech) {
903
 
                        free(ctxt->sasl_mech);
904
 
                        ctxt->sasl_mech = NULL;
905
 
                }
906
 
        }
907
 
 
908
 
        /*
909
 
         *  If LDAP_AUTH_AUTODETECT is set, it means that there was no
910
 
         *  mechanism specified in the configuration file or auto
911
 
         *  selection has been requested, so try to auto-select an
912
 
         *  auth mechanism.
913
 
         */
914
 
        if (ctxt->sasl_mech)
915
 
                conn = sasl_bind_mech(logopt, ldap, ctxt, ctxt->sasl_mech);
916
 
        else
917
 
                conn = sasl_choose_mech(logopt, ldap, ctxt);
918
 
 
919
 
        if (!conn)
920
 
                return -1;
921
 
 
922
 
        ctxt->sasl_conn = conn;
923
 
        return 0;
924
 
}
925
 
 
926
 
/*
927
 
 *  Destructor routine.  This should be called when finished with an ldap
928
 
 *  session.
929
 
 */
930
 
void autofs_sasl_dispose(struct lookup_context *ctxt)
931
 
{
932
 
        int status, ret;
933
 
 
934
 
        if (ctxt->sasl_conn) {
935
 
                sasl_dispose(&ctxt->sasl_conn);
936
 
                ctxt->sasl_conn = NULL;
937
 
        }
938
 
 
939
 
        if (ctxt->kinit_successful) {
940
 
                status = pthread_mutex_lock(&krb5cc_mutex);
941
 
                if (status)
942
 
                        fatal(status);
943
 
 
944
 
                if (--krb5cc_in_use || ctxt->client_cc)
945
 
                        ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
946
 
                else 
947
 
                        ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache);
948
 
                if (ret)
949
 
                        logmsg("krb5_cc_destroy failed with non-fatal error %d",
950
 
                             ret);
951
 
 
952
 
                status = pthread_mutex_unlock(&krb5cc_mutex);
953
 
                if (status)
954
 
                        fatal(status);
955
 
 
956
 
                krb5_free_context(ctxt->krb5ctxt);
957
 
                if (unsetenv(krb5ccenv) != 0)
958
 
                        logerr("unsetenv failed with error %d", errno);
959
 
 
960
 
                ctxt->krb5ctxt = NULL;
961
 
                ctxt->krb5_ccache = NULL;
962
 
                ctxt->kinit_done = 0;
963
 
                ctxt->kinit_successful = 0;
964
 
        }
965
 
}
966
 
 
967
 
static void *sasl_mutex_new(void)
968
 
{
969
 
        pthread_mutex_t* mutex;
970
 
 
971
 
        mutex = malloc(sizeof(pthread_mutex_t));
972
 
        if (!mutex)
973
 
                return 0;
974
 
                
975
 
        pthread_mutex_init(mutex, NULL);
976
 
 
977
 
        return (void *) mutex;
978
 
}
979
 
 
980
 
static int sasl_mutex_lock(void *mutex __attribute__((unused)))
981
 
{
982
 
        int rc;
983
 
 
984
 
        if (!mutex)
985
 
                return SASL_FAIL;
986
 
 
987
 
        rc = pthread_mutex_lock((pthread_mutex_t *) mutex);
988
 
 
989
 
        return (rc==0 ? SASL_OK : SASL_FAIL);
990
 
}
991
 
 
992
 
static int sasl_mutex_unlock(void *mutex __attribute__((unused)))
993
 
{
994
 
        int rc;
995
 
 
996
 
        if (!mutex)
997
 
                return SASL_FAIL;
998
 
 
999
 
        rc = pthread_mutex_unlock((pthread_mutex_t *) mutex);
1000
 
 
1001
 
        return (rc==0 ? SASL_OK : SASL_FAIL);
1002
 
}
1003
 
 
1004
 
static void sasl_mutex_dispose(void *mutex __attribute__((unused)))
1005
 
{
1006
 
        int rc;
1007
 
 
1008
 
        if (!mutex)
1009
 
                return;
1010
 
 
1011
 
        rc = pthread_mutex_destroy((pthread_mutex_t *) mutex);
1012
 
        if (rc == 0)
1013
 
                free(mutex);
1014
 
 
1015
 
        return;
1016
 
}
1017
 
 
1018
 
/*
1019
 
 * Initialize the sasl callbacks, which increments the global
1020
 
 * use counter.
1021
 
 */
1022
 
int autofs_sasl_client_init(unsigned logopt)
1023
 
{
1024
 
 
1025
 
        sasl_set_mutex(sasl_mutex_new,
1026
 
                       sasl_mutex_lock,
1027
 
                       sasl_mutex_unlock,
1028
 
                       sasl_mutex_dispose);
1029
 
 
1030
 
        /* Start up Cyrus SASL--only needs to be done at library load. */
1031
 
        if (sasl_client_init(callbacks) != SASL_OK) {
1032
 
                error(logopt, "sasl_client_init failed");
1033
 
                return 0;
1034
 
        }
1035
 
        return 1;
1036
 
}
1037
 
 
1038
 
/*
1039
 
 * Decrement the library reference count and free resources if
1040
 
 * we are the last to close the library.
1041
 
 */
1042
 
void autofs_sasl_done(void)
1043
 
{
1044
 
        sasl_done();
1045
 
        return;
1046
 
}
1047