Annotation of embedaddon/curl/lib/curl_sasl.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9: *
10: * This software is licensed as described in the file COPYING, which
11: * you should have received as part of this distribution. The terms
12: * are also available at https://curl.haxx.se/docs/copyright.html.
13: *
14: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15: * copies of the Software, and permit persons to whom the Software is
16: * furnished to do so, under the terms of the COPYING file.
17: *
18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19: * KIND, either express or implied.
20: *
21: * RFC2195 CRAM-MD5 authentication
22: * RFC2617 Basic and Digest Access Authentication
23: * RFC2831 DIGEST-MD5 authentication
24: * RFC4422 Simple Authentication and Security Layer (SASL)
25: * RFC4616 PLAIN authentication
26: * RFC6749 OAuth 2.0 Authorization Framework
27: * RFC7628 A Set of SASL Mechanisms for OAuth
28: * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
29: *
30: ***************************************************************************/
31:
32: #include "curl_setup.h"
33:
34: #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
35: !defined(CURL_DISABLE_POP3)
36:
37: #include <curl/curl.h>
38: #include "urldata.h"
39:
40: #include "curl_base64.h"
41: #include "curl_md5.h"
42: #include "vauth/vauth.h"
43: #include "vtls/vtls.h"
44: #include "curl_hmac.h"
45: #include "curl_sasl.h"
46: #include "warnless.h"
47: #include "strtok.h"
48: #include "sendf.h"
49: #include "non-ascii.h" /* included for Curl_convert_... prototypes */
50: /* The last 3 #include files should be in this order */
51: #include "curl_printf.h"
52: #include "curl_memory.h"
53: #include "memdebug.h"
54:
55: /* Supported mechanisms */
56: static const struct {
57: const char *name; /* Name */
58: size_t len; /* Name length */
59: unsigned int bit; /* Flag bit */
60: } mechtable[] = {
61: { "LOGIN", 5, SASL_MECH_LOGIN },
62: { "PLAIN", 5, SASL_MECH_PLAIN },
63: { "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 },
64: { "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
65: { "GSSAPI", 6, SASL_MECH_GSSAPI },
66: { "EXTERNAL", 8, SASL_MECH_EXTERNAL },
67: { "NTLM", 4, SASL_MECH_NTLM },
68: { "XOAUTH2", 7, SASL_MECH_XOAUTH2 },
69: { "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER },
70: { ZERO_NULL, 0, 0 }
71: };
72:
73: /*
74: * Curl_sasl_cleanup()
75: *
76: * This is used to cleanup any libraries or curl modules used by the sasl
77: * functions.
78: *
79: * Parameters:
80: *
81: * conn [in] - The connection data.
82: * authused [in] - The authentication mechanism used.
83: */
84: void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
85: {
86: #if defined(USE_KERBEROS5)
87: /* Cleanup the gssapi structure */
88: if(authused == SASL_MECH_GSSAPI) {
89: Curl_auth_cleanup_gssapi(&conn->krb5);
90: }
91: #endif
92:
93: #if defined(USE_NTLM)
94: /* Cleanup the NTLM structure */
95: if(authused == SASL_MECH_NTLM) {
96: Curl_auth_cleanup_ntlm(&conn->ntlm);
97: }
98: #endif
99:
100: #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
101: /* Reserved for future use */
102: (void)conn;
103: (void)authused;
104: #endif
105: }
106:
107: /*
108: * Curl_sasl_decode_mech()
109: *
110: * Convert a SASL mechanism name into a token.
111: *
112: * Parameters:
113: *
114: * ptr [in] - The mechanism string.
115: * maxlen [in] - Maximum mechanism string length.
116: * len [out] - If not NULL, effective name length.
117: *
118: * Returns the SASL mechanism token or 0 if no match.
119: */
120: unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
121: {
122: unsigned int i;
123: char c;
124:
125: for(i = 0; mechtable[i].name; i++) {
126: if(maxlen >= mechtable[i].len &&
127: !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
128: if(len)
129: *len = mechtable[i].len;
130:
131: if(maxlen == mechtable[i].len)
132: return mechtable[i].bit;
133:
134: c = ptr[mechtable[i].len];
135: if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
136: return mechtable[i].bit;
137: }
138: }
139:
140: return 0;
141: }
142:
143: /*
144: * Curl_sasl_parse_url_auth_option()
145: *
146: * Parse the URL login options.
147: */
148: CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
149: const char *value, size_t len)
150: {
151: CURLcode result = CURLE_OK;
152: size_t mechlen;
153:
154: if(!len)
155: return CURLE_URL_MALFORMAT;
156:
157: if(sasl->resetprefs) {
158: sasl->resetprefs = FALSE;
159: sasl->prefmech = SASL_AUTH_NONE;
160: }
161:
162: if(!strncmp(value, "*", len))
163: sasl->prefmech = SASL_AUTH_DEFAULT;
164: else {
165: unsigned int mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
166: if(mechbit && mechlen == len)
167: sasl->prefmech |= mechbit;
168: else
169: result = CURLE_URL_MALFORMAT;
170: }
171:
172: return result;
173: }
174:
175: /*
176: * Curl_sasl_init()
177: *
178: * Initializes the SASL structure.
179: */
180: void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
181: {
182: sasl->params = params; /* Set protocol dependent parameters */
183: sasl->state = SASL_STOP; /* Not yet running */
184: sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
185: sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
186: sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
187: sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
188: sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
189: sasl->force_ir = FALSE; /* Respect external option */
190: }
191:
192: /*
193: * state()
194: *
195: * This is the ONLY way to change SASL state!
196: */
197: static void state(struct SASL *sasl, struct connectdata *conn,
198: saslstate newstate)
199: {
200: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
201: /* for debug purposes */
202: static const char * const names[]={
203: "STOP",
204: "PLAIN",
205: "LOGIN",
206: "LOGIN_PASSWD",
207: "EXTERNAL",
208: "CRAMMD5",
209: "DIGESTMD5",
210: "DIGESTMD5_RESP",
211: "NTLM",
212: "NTLM_TYPE2MSG",
213: "GSSAPI",
214: "GSSAPI_TOKEN",
215: "GSSAPI_NO_DATA",
216: "OAUTH2",
217: "OAUTH2_RESP",
218: "CANCEL",
219: "FINAL",
220: /* LAST */
221: };
222:
223: if(sasl->state != newstate)
224: infof(conn->data, "SASL %p state change from %s to %s\n",
225: (void *)sasl, names[sasl->state], names[newstate]);
226: #else
227: (void) conn;
228: #endif
229:
230: sasl->state = newstate;
231: }
232:
233: /*
234: * Curl_sasl_can_authenticate()
235: *
236: * Check if we have enough auth data and capabilities to authenticate.
237: */
238: bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
239: {
240: /* Have credentials been provided? */
241: if(conn->bits.user_passwd)
242: return TRUE;
243:
244: /* EXTERNAL can authenticate without a user name and/or password */
245: if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
246: return TRUE;
247:
248: return FALSE;
249: }
250:
251: /*
252: * Curl_sasl_start()
253: *
254: * Calculate the required login details for SASL authentication.
255: */
256: CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
257: bool force_ir, saslprogress *progress)
258: {
259: CURLcode result = CURLE_OK;
260: struct Curl_easy *data = conn->data;
261: unsigned int enabledmechs;
262: const char *mech = NULL;
263: char *resp = NULL;
264: size_t len = 0;
265: saslstate state1 = SASL_STOP;
266: saslstate state2 = SASL_FINAL;
267: const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
268: conn->host.name;
269: const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
270: #if defined(USE_KERBEROS5) || defined(USE_NTLM)
271: const char *service = data->set.str[STRING_SERVICE_NAME] ?
272: data->set.str[STRING_SERVICE_NAME] :
273: sasl->params->service;
274: #endif
275: const char *oauth_bearer = data->set.str[STRING_BEARER];
276:
277: sasl->force_ir = force_ir; /* Latch for future use */
278: sasl->authused = 0; /* No mechanism used yet */
279: enabledmechs = sasl->authmechs & sasl->prefmech;
280: *progress = SASL_IDLE;
281:
282: /* Calculate the supported authentication mechanism, by decreasing order of
283: security, as well as the initial response where appropriate */
284: if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
285: mech = SASL_MECH_STRING_EXTERNAL;
286: state1 = SASL_EXTERNAL;
287: sasl->authused = SASL_MECH_EXTERNAL;
288:
289: if(force_ir || data->set.sasl_ir)
290: result = Curl_auth_create_external_message(data, conn->user, &resp,
291: &len);
292: }
293: else if(conn->bits.user_passwd) {
294: #if defined(USE_KERBEROS5)
295: if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
296: Curl_auth_user_contains_domain(conn->user)) {
297: sasl->mutual_auth = FALSE;
298: mech = SASL_MECH_STRING_GSSAPI;
299: state1 = SASL_GSSAPI;
300: state2 = SASL_GSSAPI_TOKEN;
301: sasl->authused = SASL_MECH_GSSAPI;
302:
303: if(force_ir || data->set.sasl_ir)
304: result = Curl_auth_create_gssapi_user_message(data, conn->user,
305: conn->passwd,
306: service,
307: data->conn->host.name,
308: sasl->mutual_auth,
309: NULL, &conn->krb5,
310: &resp, &len);
311: }
312: else
313: #endif
314: #ifndef CURL_DISABLE_CRYPTO_AUTH
315: if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
316: Curl_auth_is_digest_supported()) {
317: mech = SASL_MECH_STRING_DIGEST_MD5;
318: state1 = SASL_DIGESTMD5;
319: sasl->authused = SASL_MECH_DIGEST_MD5;
320: }
321: else if(enabledmechs & SASL_MECH_CRAM_MD5) {
322: mech = SASL_MECH_STRING_CRAM_MD5;
323: state1 = SASL_CRAMMD5;
324: sasl->authused = SASL_MECH_CRAM_MD5;
325: }
326: else
327: #endif
328: #ifdef USE_NTLM
329: if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
330: mech = SASL_MECH_STRING_NTLM;
331: state1 = SASL_NTLM;
332: state2 = SASL_NTLM_TYPE2MSG;
333: sasl->authused = SASL_MECH_NTLM;
334:
335: if(force_ir || data->set.sasl_ir)
336: result = Curl_auth_create_ntlm_type1_message(data,
337: conn->user, conn->passwd,
338: service,
339: hostname,
340: &conn->ntlm, &resp,
341: &len);
342: }
343: else
344: #endif
345: if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) {
346: mech = SASL_MECH_STRING_OAUTHBEARER;
347: state1 = SASL_OAUTH2;
348: state2 = SASL_OAUTH2_RESP;
349: sasl->authused = SASL_MECH_OAUTHBEARER;
350:
351: if(force_ir || data->set.sasl_ir)
352: result = Curl_auth_create_oauth_bearer_message(data, conn->user,
353: hostname,
354: port,
355: oauth_bearer,
356: &resp, &len);
357: }
358: else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) {
359: mech = SASL_MECH_STRING_XOAUTH2;
360: state1 = SASL_OAUTH2;
361: sasl->authused = SASL_MECH_XOAUTH2;
362:
363: if(force_ir || data->set.sasl_ir)
364: result = Curl_auth_create_xoauth_bearer_message(data, conn->user,
365: oauth_bearer,
366: &resp, &len);
367: }
368: else if(enabledmechs & SASL_MECH_PLAIN) {
369: mech = SASL_MECH_STRING_PLAIN;
370: state1 = SASL_PLAIN;
371: sasl->authused = SASL_MECH_PLAIN;
372:
373: if(force_ir || data->set.sasl_ir)
374: result = Curl_auth_create_plain_message(data, conn->sasl_authzid,
375: conn->user, conn->passwd,
376: &resp, &len);
377: }
378: else if(enabledmechs & SASL_MECH_LOGIN) {
379: mech = SASL_MECH_STRING_LOGIN;
380: state1 = SASL_LOGIN;
381: state2 = SASL_LOGIN_PASSWD;
382: sasl->authused = SASL_MECH_LOGIN;
383:
384: if(force_ir || data->set.sasl_ir)
385: result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
386: }
387: }
388:
389: if(!result && mech) {
390: if(resp && sasl->params->maxirlen &&
391: strlen(mech) + len > sasl->params->maxirlen) {
392: free(resp);
393: resp = NULL;
394: }
395:
396: result = sasl->params->sendauth(conn, mech, resp);
397: if(!result) {
398: *progress = SASL_INPROGRESS;
399: state(sasl, conn, resp ? state2 : state1);
400: }
401: }
402:
403: free(resp);
404:
405: return result;
406: }
407:
408: /*
409: * Curl_sasl_continue()
410: *
411: * Continue the authentication.
412: */
413: CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
414: int code, saslprogress *progress)
415: {
416: CURLcode result = CURLE_OK;
417: struct Curl_easy *data = conn->data;
418: saslstate newstate = SASL_FINAL;
419: char *resp = NULL;
420: const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
421: conn->host.name;
422: const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
423: #if !defined(CURL_DISABLE_CRYPTO_AUTH)
424: char *chlg = NULL;
425: size_t chlglen = 0;
426: #endif
427: #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
428: defined(USE_NTLM)
429: const char *service = data->set.str[STRING_SERVICE_NAME] ?
430: data->set.str[STRING_SERVICE_NAME] :
431: sasl->params->service;
432: char *serverdata;
433: #endif
434: size_t len = 0;
435: const char *oauth_bearer = data->set.str[STRING_BEARER];
436:
437: *progress = SASL_INPROGRESS;
438:
439: if(sasl->state == SASL_FINAL) {
440: if(code != sasl->params->finalcode)
441: result = CURLE_LOGIN_DENIED;
442: *progress = SASL_DONE;
443: state(sasl, conn, SASL_STOP);
444: return result;
445: }
446:
447: if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
448: code != sasl->params->contcode) {
449: *progress = SASL_DONE;
450: state(sasl, conn, SASL_STOP);
451: return CURLE_LOGIN_DENIED;
452: }
453:
454: switch(sasl->state) {
455: case SASL_STOP:
456: *progress = SASL_DONE;
457: return result;
458: case SASL_PLAIN:
459: result = Curl_auth_create_plain_message(data, conn->sasl_authzid,
460: conn->user, conn->passwd,
461: &resp, &len);
462: break;
463: case SASL_LOGIN:
464: result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
465: newstate = SASL_LOGIN_PASSWD;
466: break;
467: case SASL_LOGIN_PASSWD:
468: result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len);
469: break;
470: case SASL_EXTERNAL:
471: result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
472: break;
473:
474: #ifndef CURL_DISABLE_CRYPTO_AUTH
475: case SASL_CRAMMD5:
476: sasl->params->getmessage(data->state.buffer, &serverdata);
477: result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen);
478: if(!result)
479: result = Curl_auth_create_cram_md5_message(data, chlg, conn->user,
480: conn->passwd, &resp, &len);
481: free(chlg);
482: break;
483: case SASL_DIGESTMD5:
484: sasl->params->getmessage(data->state.buffer, &serverdata);
485: result = Curl_auth_create_digest_md5_message(data, serverdata,
486: conn->user, conn->passwd,
487: service,
488: &resp, &len);
489: newstate = SASL_DIGESTMD5_RESP;
490: break;
491: case SASL_DIGESTMD5_RESP:
492: resp = strdup("");
493: if(!resp)
494: result = CURLE_OUT_OF_MEMORY;
495: break;
496: #endif
497:
498: #ifdef USE_NTLM
499: case SASL_NTLM:
500: /* Create the type-1 message */
501: result = Curl_auth_create_ntlm_type1_message(data,
502: conn->user, conn->passwd,
503: service, hostname,
504: &conn->ntlm, &resp, &len);
505: newstate = SASL_NTLM_TYPE2MSG;
506: break;
507: case SASL_NTLM_TYPE2MSG:
508: /* Decode the type-2 message */
509: sasl->params->getmessage(data->state.buffer, &serverdata);
510: result = Curl_auth_decode_ntlm_type2_message(data, serverdata,
511: &conn->ntlm);
512: if(!result)
513: result = Curl_auth_create_ntlm_type3_message(data, conn->user,
514: conn->passwd, &conn->ntlm,
515: &resp, &len);
516: break;
517: #endif
518:
519: #if defined(USE_KERBEROS5)
520: case SASL_GSSAPI:
521: result = Curl_auth_create_gssapi_user_message(data, conn->user,
522: conn->passwd,
523: service,
524: data->conn->host.name,
525: sasl->mutual_auth, NULL,
526: &conn->krb5,
527: &resp, &len);
528: newstate = SASL_GSSAPI_TOKEN;
529: break;
530: case SASL_GSSAPI_TOKEN:
531: sasl->params->getmessage(data->state.buffer, &serverdata);
532: if(sasl->mutual_auth) {
533: /* Decode the user token challenge and create the optional response
534: message */
535: result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
536: NULL, NULL,
537: sasl->mutual_auth,
538: serverdata, &conn->krb5,
539: &resp, &len);
540: newstate = SASL_GSSAPI_NO_DATA;
541: }
542: else
543: /* Decode the security challenge and create the response message */
544: result = Curl_auth_create_gssapi_security_message(data, serverdata,
545: &conn->krb5,
546: &resp, &len);
547: break;
548: case SASL_GSSAPI_NO_DATA:
549: sasl->params->getmessage(data->state.buffer, &serverdata);
550: /* Decode the security challenge and create the response message */
551: result = Curl_auth_create_gssapi_security_message(data, serverdata,
552: &conn->krb5,
553: &resp, &len);
554: break;
555: #endif
556:
557: case SASL_OAUTH2:
558: /* Create the authorisation message */
559: if(sasl->authused == SASL_MECH_OAUTHBEARER) {
560: result = Curl_auth_create_oauth_bearer_message(data, conn->user,
561: hostname,
562: port,
563: oauth_bearer,
564: &resp, &len);
565:
566: /* Failures maybe sent by the server as continuations for OAUTHBEARER */
567: newstate = SASL_OAUTH2_RESP;
568: }
569: else
570: result = Curl_auth_create_xoauth_bearer_message(data, conn->user,
571: oauth_bearer,
572: &resp, &len);
573: break;
574:
575: case SASL_OAUTH2_RESP:
576: /* The continuation is optional so check the response code */
577: if(code == sasl->params->finalcode) {
578: /* Final response was received so we are done */
579: *progress = SASL_DONE;
580: state(sasl, conn, SASL_STOP);
581: return result;
582: }
583: else if(code == sasl->params->contcode) {
584: /* Acknowledge the continuation by sending a 0x01 response base64
585: encoded */
586: resp = strdup("AQ==");
587: if(!resp)
588: result = CURLE_OUT_OF_MEMORY;
589: break;
590: }
591: else {
592: *progress = SASL_DONE;
593: state(sasl, conn, SASL_STOP);
594: return CURLE_LOGIN_DENIED;
595: }
596:
597: case SASL_CANCEL:
598: /* Remove the offending mechanism from the supported list */
599: sasl->authmechs ^= sasl->authused;
600:
601: /* Start an alternative SASL authentication */
602: result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
603: newstate = sasl->state; /* Use state from Curl_sasl_start() */
604: break;
605: default:
606: failf(data, "Unsupported SASL authentication mechanism");
607: result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
608: break;
609: }
610:
611: switch(result) {
612: case CURLE_BAD_CONTENT_ENCODING:
613: /* Cancel dialog */
614: result = sasl->params->sendcont(conn, "*");
615: newstate = SASL_CANCEL;
616: break;
617: case CURLE_OK:
618: if(resp)
619: result = sasl->params->sendcont(conn, resp);
620: break;
621: default:
622: newstate = SASL_STOP; /* Stop on error */
623: *progress = SASL_DONE;
624: break;
625: }
626:
627: free(resp);
628:
629: state(sasl, conn, newstate);
630:
631: return result;
632: }
633: #endif /* protocols are enabled that use SASL */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>