Bug Summary

File:modules/cyrus-sasl.c
Location:line 356, column 11
Description:Branch condition evaluates to an uninitialized value

Annotated Source Code

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_CODE0x0031
61#ifdef LDAP_OPT_ERROR_NUMBER0x0031
62#define LDAP_OPT_RESULT_CODE0x0031 LDAP_OPT_ERROR_NUMBER0x0031
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 */
72static const char *krb5ccenv = "KRB5CCNAME";
73static const char *krb5ccval = "MEMORY:_autofstkt";
74static const char *default_client = "autofsclient";
75static pthread_mutex_t krb5cc_mutex = PTHREAD_MUTEX_INITIALIZER{ { 0, 0, 0, 0, 0, 0, { 0, 0 } } };
76static unsigned int krb5cc_in_use = 0;
77
78static int sasl_log_func(void *, int, const char *);
79static int getpass_func(sasl_conn_t *, void *, int, sasl_secret_t **);
80static int getuser_func(void *, int, const char **, unsigned *);
81
82static sasl_callback_t callbacks[] = {
83 { SASL_CB_LOG2, &sasl_log_func, NULL((void*)0) },
84 { SASL_CB_USER0x4001, &getuser_func, NULL((void*)0) },
85 { SASL_CB_AUTHNAME0x4002, &getuser_func, NULL((void*)0) },
86 { SASL_CB_PASS0x4004, &getpass_func, NULL((void*)0) },
87 { SASL_CB_LIST_END0, NULL((void*)0), NULL((void*)0) },
88};
89
90static char *sasl_auth_id, *sasl_auth_secret;
91sasl_secret_t *sasl_secret;
92
93static int
94sasl_log_func(void *context, int level, const char *message)
95{
96 switch (level) {
97 case SASL_LOG_ERR1:
98 case SASL_LOG_FAIL2:
99 logerr("%s", message)do { logmsg("%s:%d: " "%s", __FUNCTION__, 99, message); } while
(0)
;
100 break;
101 case SASL_LOG_WARN3:
102 logmsg("%s", message);
103 break;
104 case SASL_LOG_NOTE4:
105 logmsg("%s", message);
106 break;
107 case SASL_LOG_DEBUG5:
108 case SASL_LOG_TRACE6:
109 case SASL_LOG_PASS7:
110 debug(LOGOPT_DEBUG, "%s", message)do { log_debug(0x0001, "%s: " "%s", __FUNCTION__, message); }
while (0)
;
111 break;
112 default:
113 break;
114 }
115
116 return SASL_OK0;
117}
118
119static int
120getuser_func(void *context, int id, const char **result, unsigned *len)
121{
122 debug(LOGOPT_NONE, "called with context %p, id %d.", context, id)do { log_debug(0x0000, "%s: " "called with context %p, id %d."
, __FUNCTION__, context, id); } while (0)
;
123
124 switch (id) {
125 case SASL_CB_USER0x4001:
126 case SASL_CB_AUTHNAME0x4002:
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)do { log_error(0x0002, "%s: " "unknown id in request: %d", __FUNCTION__
, id); } while (0)
;
133 return SASL_FAIL-1;
134 }
135
136 return SASL_OK0;
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 */
144static int
145getpass_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)do { log_debug(0x0000, "%s: " "context %p, id %d", __FUNCTION__
, context, id); } while (0)
;
150
151 *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
152 if (!*psecret)
153 return SASL_NOMEM-2;
154
155 (*psecret)->len = strlen(sasl_auth_secret);
156 strncpy((char *)(*psecret)->data, sasl_auth_secret, len);
157
158 return SASL_OK0;
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 */
168char **
169get_server_SASL_mechanisms(unsigned logopt, LDAP *ld)
170{
171 int ret;
172 const char *saslattrlist[] = {"supportedSASLmechanisms", NULL((void*)0)};
173 LDAPMessage *results = NULL((void*)0), *entry;
174 char **mechanisms;
175
176 ret = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE((ber_int_t) 0x0000), "(objectclass=*)",
177 (char **)saslattrlist, 0,
178 NULL((void*)0), NULL((void*)0),
179 NULL((void*)0), LDAP_NO_LIMIT0, &results);
180 if (ret != LDAP_SUCCESS0x00) {
181 error(logopt, "%s", ldap_err2string(ret))do { log_error(logopt, "%s: " "%s", __FUNCTION__, ldap_err2string
(ret)); } while (0)
;
182 return NULL((void*)0);
183 }
184
185 entry = ldap_first_entry(ld, results);
186 if (entry == NULL((void*)0)) {
187 /* No root DSE. (!) */
188 ldap_msgfree(results);
189 debug(logopt,do { log_debug(logopt, "%s: " "a lookup of \"supportedSASLmechanisms\" returned "
"no results.", __FUNCTION__); } while (0)
190 "a lookup of \"supportedSASLmechanisms\" returned "do { log_debug(logopt, "%s: " "a lookup of \"supportedSASLmechanisms\" returned "
"no results.", __FUNCTION__); } while (0)
191 "no results.")do { log_debug(logopt, "%s: " "a lookup of \"supportedSASLmechanisms\" returned "
"no results.", __FUNCTION__); } while (0)
;
192 return NULL((void*)0);
193 }
194
195 mechanisms = ldap_get_values(ld, entry, "supportedSASLmechanisms");
196 ldap_msgfree(results);
197 if (mechanisms == NULL((void*)0)) {
198 /* Well, that was a waste of time. */
199 info(logopt, "No SASL authentication mechanisms are supported"do { log_info(logopt, "No SASL authentication mechanisms are supported"
" by the LDAP server."); } while (0)
200 " by the LDAP server.")do { log_info(logopt, "No SASL authentication mechanisms are supported"
" by the LDAP server."); } while (0)
;
201 return NULL((void*)0);
202 }
203
204 return mechanisms;
205}
206
207/*
208 * Returns 0 upon successful connect, -1 on failure.
209 */
210int
211do_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;
1
Variable 'bind_result' declared without an initial value
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((void*)0), auth_mech,
225 (client_cred.bv_len > 0) ?
2
'?' condition is true
226 &client_cred : NULL((void*)0),
227 NULL((void*)0), NULL((void*)0), &msgid);
228 if (ret != LDAP_SUCCESS0x00) {
3
Taking false branch
229 crit(logopt,do { log_crit(logopt, "%s: " "Error sending sasl_bind request to "
"the server: %s", __FUNCTION__, ldap_err2string(ret)); } while
(0)
230 "Error sending sasl_bind request to "do { log_crit(logopt, "%s: " "Error sending sasl_bind request to "
"the server: %s", __FUNCTION__, ldap_err2string(ret)); } while
(0)
231 "the server: %s", ldap_err2string(ret))do { log_crit(logopt, "%s: " "Error sending sasl_bind request to "
"the server: %s", __FUNCTION__, ldap_err2string(ret)); } while
(0)
;
232 return -1;
233 }
234
235 /* Wait for a result message for this bind request. */
236 results = NULL((void*)0);
237 ret = ldap_result(ld, msgid, LDAP_MSG_ALL0x01, NULL((void*)0), &results);
238 if (ret != LDAP_RES_BIND((ber_tag_t) 0x61U)) {
4
Taking false branch
239 crit(logopt,do { log_crit(logopt, "%s: " "Error while waiting for response to "
"sasl_bind request: %s", __FUNCTION__, ldap_err2string(ret))
; } while (0)
240 "Error while waiting for response to "do { log_crit(logopt, "%s: " "Error while waiting for response to "
"sasl_bind request: %s", __FUNCTION__, ldap_err2string(ret))
; } while (0)
241 "sasl_bind request: %s", ldap_err2string(ret))do { log_crit(logopt, "%s: " "Error while waiting for response to "
"sasl_bind request: %s", __FUNCTION__, ldap_err2string(ret))
; } while (0)
;
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((void*)0);
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_SUCCESS0x00) {
5
Taking false branch
263 /* Mozilla? */
264 ret = ldap_get_option(ld, LDAP_OPT_RESULT_CODE0x0031,
265 &bind_result);
266 if (ret != LDAP_SUCCESS0x00) {
267 crit(logopt,do { log_crit(logopt, "%s: " "Error retrieving response to sasl_bind "
"request: %s", __FUNCTION__, ldap_err2string(ret)); } while (
0)
268 "Error retrieving response to sasl_bind "do { log_crit(logopt, "%s: " "Error retrieving response to sasl_bind "
"request: %s", __FUNCTION__, ldap_err2string(ret)); } while (
0)
269 "request: %s", ldap_err2string(ret))do { log_crit(logopt, "%s: " "Error retrieving response to sasl_bind "
"request: %s", __FUNCTION__, ldap_err2string(ret)); } while (
0)
;
270 ret = -1;
271 break;
272 }
273 } else {
274 /* OpenLDAP? */
275 switch (ret) {
6
Control jumps to the 'default' case at line 279
276 case LDAP_SASL_BIND_IN_PROGRESS0x0e:
277 bind_result = ret;
278 break;
279 default:
280 warn(logopt,do { log_warn(logopt, "Error parsing response to sasl_bind " "request: %s."
, ldap_err2string(ret)); } while (0)
281 "Error parsing response to sasl_bind "do { log_warn(logopt, "Error parsing response to sasl_bind " "request: %s."
, ldap_err2string(ret)); } while (0)
282 "request: %s.", ldap_err2string(ret))do { log_warn(logopt, "Error parsing response to sasl_bind " "request: %s."
, ldap_err2string(ret)); } while (0)
;
283 break;
7
Execution continues on line 293
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((void*)0) && 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_CONTINUE1;
300
301 if (have_data && !expected_data) {
8
Taking false branch
302 warn(logopt,do { log_warn(logopt, "The LDAP server sent data in response to our "
"bind request, but indicated that the bind was " "complete. LDAP SASL bind with mechansim %s "
"failed.", auth_mech); } while (0)
303 "The LDAP server sent data in response to our "do { log_warn(logopt, "The LDAP server sent data in response to our "
"bind request, but indicated that the bind was " "complete. LDAP SASL bind with mechansim %s "
"failed.", auth_mech); } while (0)
304 "bind request, but indicated that the bind was "do { log_warn(logopt, "The LDAP server sent data in response to our "
"bind request, but indicated that the bind was " "complete. LDAP SASL bind with mechansim %s "
"failed.", auth_mech); } while (0)
305 "complete. LDAP SASL bind with mechansim %s "do { log_warn(logopt, "The LDAP server sent data in response to our "
"bind request, but indicated that the bind was " "complete. LDAP SASL bind with mechansim %s "
"failed.", auth_mech); } while (0)
306 "failed.", auth_mech)do { log_warn(logopt, "The LDAP server sent data in response to our "
"bind request, but indicated that the bind was " "complete. LDAP SASL bind with mechansim %s "
"failed.", auth_mech); } while (0)
;
307 ret = -1;
308 break;
309 }
310 if (expected_data && !have_data) {
9
Taking false branch
311 warn(logopt,do { log_warn(logopt, "The LDAP server indicated that the LDAP SASL "
"bind was incomplete, but did not provide the " "required data to proceed. LDAP SASL bind with "
"mechanism %s failed.", auth_mech); } while (0)
312 "The LDAP server indicated that the LDAP SASL "do { log_warn(logopt, "The LDAP server indicated that the LDAP SASL "
"bind was incomplete, but did not provide the " "required data to proceed. LDAP SASL bind with "
"mechanism %s failed.", auth_mech); } while (0)
313 "bind was incomplete, but did not provide the "do { log_warn(logopt, "The LDAP server indicated that the LDAP SASL "
"bind was incomplete, but did not provide the " "required data to proceed. LDAP SASL bind with "
"mechanism %s failed.", auth_mech); } while (0)
314 "required data to proceed. LDAP SASL bind with "do { log_warn(logopt, "The LDAP server indicated that the LDAP SASL "
"bind was incomplete, but did not provide the " "required data to proceed. LDAP SASL bind with "
"mechanism %s failed.", auth_mech); } while (0)
315 "mechanism %s failed.", auth_mech)do { log_warn(logopt, "The LDAP server indicated that the LDAP SASL "
"bind was incomplete, but did not provide the " "required data to proceed. LDAP SASL bind with "
"mechanism %s failed.", auth_mech); } while (0)
;
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_CONTINUE1) &&
10
Taking false branch
323 ((bind_result == LDAP_SUCCESS0x00) ||
324 (bind_result == LDAP_SASL_BIND_IN_PROGRESS0x0e))) {
325 if (server_cred != NULL((void*)0)) {
326 temp_cred = *server_cred;
327 } else {
328 temp_cred.bv_len = 0;
329 temp_cred.bv_val = NULL((void*)0);
330 }
331 sasl_result = sasl_client_step(conn,
332 temp_cred.bv_val,
333 temp_cred.bv_len,
334 NULL((void*)0),
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_PROGRESS0x0e)) {
343 warn(logopt,do { log_warn(logopt, "We have data for the server, " "but it thinks we are done!"
); } while (0)
344 "We have data for the server, "do { log_warn(logopt, "We have data for the server, " "but it thinks we are done!"
); } while (0)
345 "but it thinks we are done!")do { log_warn(logopt, "We have data for the server, " "but it thinks we are done!"
); } while (0)
;
346 /* XXX should print out debug data here */
347 ret = -1;
348 }
349 }
350
351 if (server_cred && server_cred->bv_len > 0) {
11
Taking false branch
352 ber_bvfree(server_cred);
353 server_cred = NULL((void*)0);
354 }
355
356 } while ((bind_result == LDAP_SASL_BIND_IN_PROGRESS0x0e) ||
12
Branch condition evaluates to an uninitialized value
357 (sasl_result == SASL_CONTINUE1));
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 */
376int
377sasl_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,do { log_debug(logopt, "%s: " "initializing kerberos ticket: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
390 "initializing kerberos ticket: client principal %s",do { log_debug(logopt, "%s: " "initializing kerberos ticket: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
391 ctxt->client_princ ? ctxt->client_princ : default_client)do { log_debug(logopt, "%s: " "initializing kerberos ticket: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
;
392
393 ret = krb5_init_context(&ctxt->krb5ctxt);
394 if (ret) {
395 error(logopt, "krb5_init_context failed with %d", ret)do { log_error(logopt, "%s: " "krb5_init_context failed with %d"
, __FUNCTION__, ret); } while (0)
;
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",do { log_error(logopt, "%s: " "krb5_cc_resolve failed with error %d"
, __FUNCTION__, ret); } while (0)
402 ret)do { log_error(logopt, "%s: " "krb5_cc_resolve failed with error %d"
, __FUNCTION__, ret); } while (0)
;
403 krb5_free_context(ctxt->krb5ctxt);
404 return -1;
405 }
406
407 if (ctxt->client_princ) {
408 debug(logopt,do { log_debug(logopt, "%s: " "calling krb5_parse_name on client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
409 "calling krb5_parse_name on client principal %s",do { log_debug(logopt, "%s: " "calling krb5_parse_name on client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
410 ctxt->client_princ)do { log_debug(logopt, "%s: " "calling krb5_parse_name on client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
;
411
412 ret = krb5_parse_name(ctxt->krb5ctxt, ctxt->client_princ,
413 &krb5_client_princ);
414 if (ret) {
415 error(logopt,do { log_error(logopt, "%s: " "krb5_parse_name failed for " "specified client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
416 "krb5_parse_name failed for "do { log_error(logopt, "%s: " "krb5_parse_name failed for " "specified client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
417 "specified client principal %s",do { log_error(logopt, "%s: " "krb5_parse_name failed for " "specified client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
418 ctxt->client_princ)do { log_error(logopt, "%s: " "krb5_parse_name failed for " "specified client principal %s"
, __FUNCTION__, ctxt->client_princ); } while (0)
;
419 goto out_cleanup_cc;
420 }
421 } else {
422 char *tmp_name = NULL((void*)0);
423
424 debug(logopt,do { log_debug(logopt, "%s: " "calling krb5_sname_to_principal using defaults"
, __FUNCTION__); } while (0)
425 "calling krb5_sname_to_principal using defaults")do { log_debug(logopt, "%s: " "calling krb5_sname_to_principal using defaults"
, __FUNCTION__); } while (0)
;
426
427 ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL((void*)0),
428 default_client, KRB5_NT_SRV_HST3,
429 &krb5_client_princ);
430 if (ret) {
431 error(logopt,do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
432 "krb5_sname_to_principal failed for "do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
433 "%s with error %d", default_client, ret)do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
;
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,do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
442 "krb5_unparse_name failed with error %d",do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
443 ret)do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
;
444 goto out_cleanup_client_princ;
445 }
446
447 debug(logopt,do { log_debug(logopt, "%s: " "principal used for authentication: %s"
, __FUNCTION__, tmp_name); } while (0)
448 "principal used for authentication: %s", tmp_name)do { log_debug(logopt, "%s: " "principal used for authentication: %s"
, __FUNCTION__, tmp_name); } while (0)
;
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)(&(krb5_client_princ)->realm)->length,
456 krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)(&(krb5_client_princ)->realm)->data,
457 strlen(KRB5_TGS_NAME"krbtgt"), KRB5_TGS_NAME"krbtgt",
458 krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)(&(krb5_client_princ)->realm)->length,
459 krb5_princ_realm(ctxt->krb5ctxt, krb5_client_princ)(&(krb5_client_princ)->realm)->data,
460 0);
461 if (ret) {
462 error(logopt,do { log_error(logopt, "%s: " "krb5_build_principal failed with error %d"
, __FUNCTION__, ret); } while (0)
463 "krb5_build_principal failed with error %d", ret)do { log_error(logopt, "%s: " "krb5_build_principal failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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",do { log_error(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
470 ret)do { log_error(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
;
471 goto out_cleanup_client_princ;
472 }
473
474 debug(logopt, "Using tgs name %s", tgs_name)do { log_debug(logopt, "%s: " "Using tgs name %s", __FUNCTION__
, tgs_name); } while (0)
;
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((void*)0) /*keytab*/,
480 0 /* relative start time */,
481 tgs_name, NULL((void*)0));
482 if (ret) {
483 error(logopt,do { log_error(logopt, "%s: " "krb5_get_init_creds_keytab failed with error %d"
, __FUNCTION__, ret); } while (0)
484 "krb5_get_init_creds_keytab failed with error %d",do { log_error(logopt, "%s: " "krb5_get_init_creds_keytab failed with error %d"
, __FUNCTION__, ret); } while (0)
485 ret)do { log_error(logopt, "%s: " "krb5_get_init_creds_keytab failed with error %d"
, __FUNCTION__, ret); } while (0)
;
486 goto out_cleanup_unparse;
487 }
488
489 status = pthread_mutex_lock(&krb5cc_mutex);
490 if (status)
491 fatal(status)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 491, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 491, "cyrus-sasl.c"); abort(); } while(0)
;
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)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 500, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 500, "cyrus-sasl.c"); abort(); } while(0)
;
501
502 if (ret) {
503 error(logopt,do { log_error(logopt, "%s: " "krb5_cc_initialize failed with error %d"
, __FUNCTION__, ret); } while (0)
504 "krb5_cc_initialize failed with error %d", ret)do { log_error(logopt, "%s: " "krb5_cc_initialize failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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,do { log_error(logopt, "%s: " "krb5_cc_store_cred failed with error %d"
, __FUNCTION__, ret); } while (0)
512 "krb5_cc_store_cred failed with error %d", ret)do { log_error(logopt, "%s: " "krb5_cc_store_cred failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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)do { log_error(logopt, "%s: " "setenv failed with %d", __FUNCTION__
, (*__errno_location ())); } while (0)
;
520 goto out_cleanup_creds;
521 }
522 ctxt->kinit_successful = 1;
523
524 debug(logopt, "Kerberos authentication was successful!")do { log_debug(logopt, "%s: " "Kerberos authentication was successful!"
, __FUNCTION__); } while (0)
;
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
533out_cleanup_creds:
534 krb5cc_in_use--;
535 krb5_free_cred_contents(ctxt->krb5ctxt, &my_creds);
536out_cleanup_unparse:
537 krb5_free_principal(ctxt->krb5ctxt, tgs_princ);
538 krb5_free_unparsed_name(ctxt->krb5ctxt, tgs_name);
539out_cleanup_client_princ:
540 krb5_free_principal(ctxt->krb5ctxt, krb5_client_princ);
541out_cleanup_cc:
542 status = pthread_mutex_lock(&krb5cc_mutex);
543 if (status)
544 fatal(status)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 544, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 544, "cyrus-sasl.c"); abort(); } while(0)
;
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,do { log_warn(logopt, "krb5_cc_destroy failed with non-fatal error %d"
, ret); } while (0)
552 "krb5_cc_destroy failed with non-fatal error %d", ret)do { log_warn(logopt, "krb5_cc_destroy failed with non-fatal error %d"
, ret); } while (0)
;
553
554 status = pthread_mutex_unlock(&krb5cc_mutex);
555 if (status)
556 fatal(status)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 556, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 556, "cyrus-sasl.c"); abort(); } while(0)
;
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 */
572int
573sasl_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,do { log_debug(logopt, "%s: " "using external credential cache for auth: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
585 "using external credential cache for auth: client principal %s",do { log_debug(logopt, "%s: " "using external credential cache for auth: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
586 ctxt->client_princ ? ctxt->client_princ : default_client)do { log_debug(logopt, "%s: " "using external credential cache for auth: client principal %s"
, __FUNCTION__, ctxt->client_princ ? ctxt->client_princ
: default_client); } while (0)
;
587
588 ret = krb5_init_context(&ctxt->krb5ctxt);
589 if (ret) {
590 error(logopt, "krb5_init_context failed with %d", ret)do { log_error(logopt, "%s: " "krb5_init_context failed with %d"
, __FUNCTION__, ret); } while (0)
;
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",do { log_error(logopt, "%s: " "krb5_cc_resolve failed with error %d"
, __FUNCTION__, ret); } while (0)
597 ret)do { log_error(logopt, "%s: " "krb5_cc_resolve failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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)do { log_error(logopt, "%s: " "krb5_cc_get_principal failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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)do { log_error(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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)do { log_debug(logopt, "%s: " "external credential cache default principal %s"
, __FUNCTION__, cc_princ); } while (0)
;
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,do { log_debug(logopt, "%s: " "calling krb5_sname_to_principal using defaults"
, __FUNCTION__); } while (0)
631 "calling krb5_sname_to_principal using defaults")do { log_debug(logopt, "%s: " "calling krb5_sname_to_principal using defaults"
, __FUNCTION__); } while (0)
;
632
633 ret = krb5_sname_to_principal(ctxt->krb5ctxt, NULL((void*)0),
634 default_client, KRB5_NT_SRV_HST3,
635 &krb5_client_princ);
636 if (ret) {
637 error(logopt,do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
638 "krb5_sname_to_principal failed for "do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
639 "%s with error %d", default_client, ret)do { log_error(logopt, "%s: " "krb5_sname_to_principal failed for "
"%s with error %d", __FUNCTION__, default_client, ret); } while
(0)
;
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,do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
651 "krb5_unparse_name failed with error %d",do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
652 ret)do { log_debug(logopt, "%s: " "krb5_unparse_name failed with error %d"
, __FUNCTION__, ret); } while (0)
;
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,do { log_debug(logopt, "%s: " "principal used for authentication: %s"
, __FUNCTION__, client_princ); } while (0)
661 "principal used for authentication: %s", client_princ)do { log_debug(logopt, "%s: " "principal used for authentication: %s"
, __FUNCTION__, client_princ); } while (0)
;
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,do { log_error(logopt, "%s: " "configured client principal %s "
, __FUNCTION__, ctxt->client_princ); } while (0)
672 "configured client principal %s ",do { log_error(logopt, "%s: " "configured client principal %s "
, __FUNCTION__, ctxt->client_princ); } while (0)
673 ctxt->client_princ)do { log_error(logopt, "%s: " "configured client principal %s "
, __FUNCTION__, ctxt->client_princ); } while (0)
;
674 error(logopt,do { log_error(logopt, "%s: " "external credential cache default principal %s"
, __FUNCTION__, cc_princ); } while (0)
675 "external credential cache default principal %s",do { log_error(logopt, "%s: " "external credential cache default principal %s"
, __FUNCTION__, cc_princ); } while (0)
676 cc_princ)do { log_error(logopt, "%s: " "external credential cache default principal %s"
, __FUNCTION__, cc_princ); } while (0)
;
677 error(logopt,do { log_error(logopt, "%s: " "cannot use credential cache, external "
"default principal does not match configured " "principal", __FUNCTION__
); } while (0)
678 "cannot use credential cache, external "do { log_error(logopt, "%s: " "cannot use credential cache, external "
"default principal does not match configured " "principal", __FUNCTION__
); } while (0)
679 "default principal does not match configured "do { log_error(logopt, "%s: " "cannot use credential cache, external "
"default principal does not match configured " "principal", __FUNCTION__
); } while (0)
680 "principal")do { log_error(logopt, "%s: " "cannot use credential cache, external "
"default principal does not match configured " "principal", __FUNCTION__
); } while (0)
;
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)do { log_error(logopt, "%s: " "setenv failed with %d", __FUNCTION__
, (*__errno_location ())); } while (0)
;
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!")do { log_debug(logopt, "%s: " "Kerberos authentication was successful!"
, __FUNCTION__); } while (0)
;
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 */
715sasl_conn_t *
716sasl_bind_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt, const char *mech)
717{
718 sasl_conn_t *conn;
719 char *tmp, *host = NULL((void*)0);
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((void*)0);
732 }
733
734 debug(logopt, "Attempting sasl bind with mechanism %s", mech)do { log_debug(logopt, "%s: " "Attempting sasl bind with mechanism %s"
, __FUNCTION__, mech); } while (0)
;
735
736 result = ldap_get_option(ldap, LDAP_OPT_HOST_NAME0x0030, &host);
737 if (result != LDAP_OPT_SUCCESS0 || !host) {
738 debug(logopt, "failed to get hostname for connection")do { log_debug(logopt, "%s: " "failed to get hostname for connection"
, __FUNCTION__); } while (0)
;
739 return NULL((void*)0);
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((void*)0), NULL((void*)0), NULL((void*)0), 0, &conn);
756 if (result != SASL_OK0) {
757 error(logopt, "sasl_client_new failed with error %d",do { log_error(logopt, "%s: " "sasl_client_new failed with error %d"
, __FUNCTION__, result); } while (0)
758 result)do { log_error(logopt, "%s: " "sasl_client_new failed with error %d"
, __FUNCTION__, result); } while (0)
;
759 ldap_memfree(host);
760 return NULL((void*)0);
761 }
762
763 chosen_mech = NULL((void*)0);
764 result = sasl_client_start(conn, mech, NULL((void*)0),
765 &clientout, &clientoutlen, &chosen_mech);
766
767 /* OK and CONTINUE are the only non-fatal return codes here. */
768 if ((result != SASL_OK0) && (result != SASL_CONTINUE1)) {
769 warn(logopt, "sasl_client_start failed for %s", host)do { log_warn(logopt, "sasl_client_start failed for %s", host
); } while (0)
;
770 debug(logopt, "sasl_client_start: %s", sasl_errdetail(conn))do { log_debug(logopt, "%s: " "sasl_client_start: %s", __FUNCTION__
, sasl_errdetail(conn)); } while (0)
;
771 ldap_memfree(host);
772 sasl_dispose(&conn);
773 return NULL((void*)0);
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",do { log_debug(logopt, "%s: " "sasl bind with mechanism %s succeeded"
, __FUNCTION__, chosen_mech); } while (0)
781 chosen_mech)do { log_debug(logopt, "%s: " "sasl bind with mechanism %s succeeded"
, __FUNCTION__, chosen_mech); } while (0)
;
782 return conn;
783 }
784
785 info(logopt, "sasl bind with mechanism %s failed", mech)do { log_info(logopt, "sasl bind with mechanism %s failed", mech
); } while (0)
;
786
787 /* sasl bind failed */
788 ldap_memfree(host);
789 sasl_dispose(&conn);
790
791 return NULL((void*)0);
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 */
798sasl_conn_t *
799sasl_choose_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
800{
801 sasl_conn_t *conn;
802 int authenticated;
803 int i;
804 char **mechanisms;
805
806 mechanisms = get_server_SASL_mechanisms(logopt, ldap);
807 if (!mechanisms)
808 return NULL((void*)0);
809
810 /* Try each supported mechanism in turn. */
811 authenticated = 0;
812 for (i = 0; mechanisms[i] != NULL((void*)0); 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 "do { log_crit(logopt, "%s: " "Successfully authenticated with "
"mechanism %s, but failed to allocate " "memory to hold the mechanism type."
, __FUNCTION__, mechanisms[i]); } while (0)
827 "mechanism %s, but failed to allocate "do { log_crit(logopt, "%s: " "Successfully authenticated with "
"mechanism %s, but failed to allocate " "memory to hold the mechanism type."
, __FUNCTION__, mechanisms[i]); } while (0)
828 "memory to hold the mechanism type.",do { log_crit(logopt, "%s: " "Successfully authenticated with "
"mechanism %s, but failed to allocate " "memory to hold the mechanism type."
, __FUNCTION__, mechanisms[i]); } while (0)
829 mechanisms[i])do { log_crit(logopt, "%s: " "Successfully authenticated with "
"mechanism %s, but failed to allocate " "memory to hold the mechanism type."
, __FUNCTION__, mechanisms[i]); } while (0)
;
830 sasl_dispose(&conn);
831 ldap_value_free(mechanisms);
832 return NULL((void*)0);
833 }
834 authenticated = 1;
835 break;
836 }
837 debug(logopt, "Failed to authenticate with mech %s",do { log_debug(logopt, "%s: " "Failed to authenticate with mech %s"
, __FUNCTION__, mechanisms[i]); } while (0)
838 mechanisms[i])do { log_debug(logopt, "%s: " "Failed to authenticate with mech %s"
, __FUNCTION__, mechanisms[i]); } while (0)
;
839 }
840
841 debug(logopt, "authenticated: %d, sasl_mech: %s",do { log_debug(logopt, "%s: " "authenticated: %d, sasl_mech: %s"
, __FUNCTION__, authenticated, ctxt->sasl_mech); } while (
0)
842 authenticated, ctxt->sasl_mech)do { log_debug(logopt, "%s: " "authenticated: %d, sasl_mech: %s"
, __FUNCTION__, authenticated, ctxt->sasl_mech); } while (
0)
;
843
844 ldap_value_free(mechanisms);
845 return conn;
846}
847
848int
849autofs_sasl_bind(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
850{
851 sasl_conn_t *conn;
852
853 if (!ctxt->sasl_mech)
854 return -1;
855
856 conn = sasl_bind_mech(logopt, ldap, ctxt, ctxt->sasl_mech);
857 if (!conn)
858 return -1;
859
860 ctxt->sasl_conn = conn;
861 return 0;
862}
863
864/*
865 * Routine called when unbinding an ldap connection.
866 */
867void
868autofs_sasl_unbind(struct lookup_context *ctxt)
869{
870 if (ctxt->sasl_conn) {
871 sasl_dispose(&ctxt->sasl_conn);
872 ctxt->sasl_conn = NULL((void*)0);
873 }
874}
875
876/*
877 * Given a lookup context that has been initialized with any user-specified
878 * parameters, figure out which sasl mechanism to use. Then, initialize
879 * the necessary parameters to authenticate with the chosen mechanism.
880 *
881 * Return Values:
882 * 0 - Success
883 * -1 - Failure
884 */
885int
886autofs_sasl_init(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt)
887{
888 sasl_conn_t *conn;
889
890 sasl_auth_id = ctxt->user;
891 sasl_auth_secret = ctxt->secret;
892
893 /*
894 * If LDAP_AUTH_AUTODETECT is set, it means that there was no
895 * mechanism specified in the configuration file or auto
896 * selection has been requested, so try to auto-select an
897 * auth mechanism.
898 */
899 if (!(ctxt->auth_required & LDAP_AUTH_AUTODETECT0x0004))
900 conn = sasl_bind_mech(logopt, ldap, ctxt, ctxt->sasl_mech);
901 else {
902 if (ctxt->sasl_mech) {
903 free(ctxt->sasl_mech);
904 ctxt->sasl_mech = NULL((void*)0);
905 }
906 conn = sasl_choose_mech(logopt, ldap, ctxt);
907 }
908
909 if (conn) {
910 sasl_dispose(&conn);
911 return 0;
912 }
913
914 return -1;
915}
916
917/*
918 * Destructor routine. This should be called when finished with an ldap
919 * session.
920 */
921void autofs_sasl_dispose(struct lookup_context *ctxt)
922{
923 int status, ret;
924
925 if (ctxt && ctxt->sasl_conn) {
926 sasl_dispose(&ctxt->sasl_conn);
927 ctxt->sasl_conn = NULL((void*)0);
928 }
929
930 if (ctxt->kinit_successful) {
931 status = pthread_mutex_lock(&krb5cc_mutex);
932 if (status)
933 fatal(status)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 933, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 933, "cyrus-sasl.c"); abort(); } while(0)
;
934
935 if (--krb5cc_in_use || ctxt->client_cc)
936 ret = krb5_cc_close(ctxt->krb5ctxt, ctxt->krb5_ccache);
937 else
938 ret = krb5_cc_destroy(ctxt->krb5ctxt, ctxt->krb5_ccache);
939 if (ret)
940 logmsg("krb5_cc_destroy failed with non-fatal error %d",
941 ret);
942
943 status = pthread_mutex_unlock(&krb5cc_mutex);
944 if (status)
945 fatal(status)do { if (status == 35) { logmsg("deadlock detected " "at line %d in %s, dumping core."
, 945, "cyrus-sasl.c"); dump_core(); } logmsg("unexpected pthreads error: %d at %d "
"in %s", status, 945, "cyrus-sasl.c"); abort(); } while(0)
;
946
947 krb5_free_context(ctxt->krb5ctxt);
948 if (unsetenv(krb5ccenv) != 0)
949 logerr("unsetenv failed with error %d", errno)do { logmsg("%s:%d: " "unsetenv failed with error %d", __FUNCTION__
, 949, (*__errno_location ())); } while (0)
;
950
951 ctxt->krb5ctxt = NULL((void*)0);
952 ctxt->krb5_ccache = NULL((void*)0);
953 ctxt->kinit_done = 0;
954 ctxt->kinit_successful = 0;
955 }
956}
957
958/*
959 * Initialize the sasl callbacks, which increments the global
960 * use counter.
961 */
962int autofs_sasl_client_init(unsigned logopt)
963{
964 /* Start up Cyrus SASL--only needs to be done at library load. */
965 if (sasl_client_init(callbacks) != SASL_OK0) {
966 error(logopt, "sasl_client_init failed")do { log_error(logopt, "%s: " "sasl_client_init failed", __FUNCTION__
); } while (0)
;
967 return 0;
968 }
969 return 1;
970}
971
972/*
973 * Decrement the library reference count and free resources if
974 * we are the last to close the library.
975 */
976void autofs_sasl_done(void)
977{
978 sasl_done();
979 return;
980}
981