File: | modules/cyrus-sasl.c |
Location: | line 356, column 11 |
Description: | Branch condition evaluates to an uninitialized value |
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 | */ | ||
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{ { 0, 0, 0, 0, 0, 0, { 0, 0 } } }; | ||
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_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 | |||
90 | static char *sasl_auth_id, *sasl_auth_secret; | ||
91 | sasl_secret_t *sasl_secret; | ||
92 | |||
93 | static int | ||
94 | sasl_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 | |||
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)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 | */ | ||
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)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 | */ | ||
168 | char ** | ||
169 | get_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 | */ | ||
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((void*)0), auth_mech, | ||
225 | (client_cred.bv_len > 0) ? | ||
| |||
226 | &client_cred : NULL((void*)0), | ||
227 | NULL((void*)0), NULL((void*)0), &msgid); | ||
228 | if (ret != LDAP_SUCCESS0x00) { | ||
| |||
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)) { | ||
| |||
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) { | ||
| |||
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) { | ||
| |||
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; | ||
| |||
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) { | ||
| |||
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) { | ||
| |||
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) && | ||
| |||
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) { | ||
| |||
352 | ber_bvfree(server_cred); | ||
353 | server_cred = NULL((void*)0); | ||
354 | } | ||
355 | |||
356 | } while ((bind_result == LDAP_SASL_BIND_IN_PROGRESS0x0e) || | ||
| |||
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 | */ | ||
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,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 | |||
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)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 | */ | ||
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,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 | */ | ||
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((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 | */ | ||
798 | sasl_conn_t * | ||
799 | sasl_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 | |||
848 | int | ||
849 | autofs_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 | */ | ||
867 | void | ||
868 | autofs_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 | */ | ||
885 | int | ||
886 | autofs_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 | */ | ||
921 | void 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 | */ | ||
962 | int 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 | */ | ||
976 | void autofs_sasl_done(void) | ||
977 | { | ||
978 | sasl_done(); | ||
979 | return; | ||
980 | } | ||
981 |