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