Revert "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 "strequal.h"
46 #include "rawstr.h"
47 #include "sendf.h"
48 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
49 /* The last 3 #include files should be in this order */
50 #include "curl_printf.h"
51 #include "curl_memory.h"
52 #include "memdebug.h"
53
54 /* Supported mechanisms */
55 const struct {
56   const char   *name;  /* Name */
57   size_t        len;   /* Name length */
58   unsigned int  bit;   /* Flag bit */
59 } mechtable[] = {
60   { "LOGIN",        5,  SASL_MECH_LOGIN },
61   { "PLAIN",        5,  SASL_MECH_PLAIN },
62   { "CRAM-MD5",     8,  SASL_MECH_CRAM_MD5 },
63   { "DIGEST-MD5",   10, SASL_MECH_DIGEST_MD5 },
64   { "GSSAPI",       6,  SASL_MECH_GSSAPI },
65   { "EXTERNAL",     8,  SASL_MECH_EXTERNAL },
66   { "NTLM",         4,  SASL_MECH_NTLM },
67   { "XOAUTH2",      7,  SASL_MECH_XOAUTH2 },
68   { "OAUTHBEARER",  11, SASL_MECH_OAUTHBEARER },
69   { ZERO_NULL,      0,  0 }
70 };
71
72 /*
73  * Curl_sasl_cleanup()
74  *
75  * This is used to cleanup any libraries or curl modules used by the sasl
76  * functions.
77  *
78  * Parameters:
79  *
80  * conn     [in]     - The connection data.
81  * authused [in]     - The authentication mechanism used.
82  */
83 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
84 {
85 #if defined(USE_KERBEROS5)
86   /* Cleanup the gssapi structure */
87   if(authused == SASL_MECH_GSSAPI) {
88     Curl_auth_gssapi_cleanup(&conn->krb5);
89   }
90 #endif
91
92 #if defined(USE_NTLM)
93   /* Cleanup the NTLM structure */
94   if(authused == SASL_MECH_NTLM) {
95     Curl_auth_ntlm_cleanup(&conn->ntlm);
96   }
97 #endif
98
99 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
100   /* Reserved for future use */
101   (void)conn;
102   (void)authused;
103 #endif
104 }
105
106 /*
107  * Curl_sasl_decode_mech()
108  *
109  * Convert a SASL mechanism name into a token.
110  *
111  * Parameters:
112  *
113  * ptr    [in]     - The mechanism string.
114  * maxlen [in]     - Maximum mechanism string length.
115  * len    [out]    - If not NULL, effective name length.
116  *
117  * Returns the SASL mechanism token or 0 if no match.
118  */
119 unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
120 {
121   unsigned int i;
122   char c;
123
124   for(i = 0; mechtable[i].name; i++) {
125     if(maxlen >= mechtable[i].len &&
126        !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
127       if(len)
128         *len = mechtable[i].len;
129
130       if(maxlen == mechtable[i].len)
131         return mechtable[i].bit;
132
133       c = ptr[mechtable[i].len];
134       if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
135         return mechtable[i].bit;
136     }
137   }
138
139   return 0;
140 }
141
142 /*
143  * Curl_sasl_parse_url_auth_option()
144  *
145  * Parse the URL login options.
146  */
147 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
148                                          const char *value, size_t len)
149 {
150   CURLcode result = CURLE_OK;
151   unsigned int mechbit;
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(strnequal(value, "*", len))
163     sasl->prefmech = SASL_AUTH_DEFAULT;
164   else {
165     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 #if defined(USE_KERBEROS5)
268   const char* service = data->set.str[STRING_SERVICE_NAME] ?
269                         data->set.str[STRING_SERVICE_NAME] :
270                         sasl->params->service;
271 #endif
272
273   sasl->force_ir = force_ir;    /* Latch for future use */
274   sasl->authused = 0;           /* No mechanism used yet */
275   enabledmechs = sasl->authmechs & sasl->prefmech;
276   *progress = SASL_IDLE;
277
278   /* Calculate the supported authentication mechanism, by decreasing order of
279      security, as well as the initial response where appropriate */
280   if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
281     mech = SASL_MECH_STRING_EXTERNAL;
282     state1 = SASL_EXTERNAL;
283     sasl->authused = SASL_MECH_EXTERNAL;
284
285     if(force_ir || data->set.sasl_ir)
286       result = Curl_auth_create_external_message(data, conn->user, &resp,
287                                                  &len);
288   }
289   else if(conn->bits.user_passwd) {
290 #if defined(USE_KERBEROS5)
291     if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
292        Curl_auth_user_contains_domain(conn->user)) {
293       sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
294       mech = SASL_MECH_STRING_GSSAPI;
295       state1 = SASL_GSSAPI;
296       state2 = SASL_GSSAPI_TOKEN;
297       sasl->authused = SASL_MECH_GSSAPI;
298
299       if(force_ir || data->set.sasl_ir)
300         result = Curl_auth_create_gssapi_user_message(data, conn->user,
301                                                       conn->passwd,
302                                                       service,
303                                                       data->easy_conn->
304                                                             host.name,
305                                                       sasl->mutual_auth,
306                                                       NULL, &conn->krb5,
307                                                       &resp, &len);
308     }
309     else
310 #endif
311 #ifndef CURL_DISABLE_CRYPTO_AUTH
312     if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
313        Curl_auth_is_digest_supported()) {
314       mech = SASL_MECH_STRING_DIGEST_MD5;
315       state1 = SASL_DIGESTMD5;
316       sasl->authused = SASL_MECH_DIGEST_MD5;
317     }
318     else if(enabledmechs & SASL_MECH_CRAM_MD5) {
319       mech = SASL_MECH_STRING_CRAM_MD5;
320       state1 = SASL_CRAMMD5;
321       sasl->authused = SASL_MECH_CRAM_MD5;
322     }
323     else
324 #endif
325 #ifdef USE_NTLM
326     if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
327       mech = SASL_MECH_STRING_NTLM;
328       state1 = SASL_NTLM;
329       state2 = SASL_NTLM_TYPE2MSG;
330       sasl->authused = SASL_MECH_NTLM;
331
332       if(force_ir || data->set.sasl_ir)
333         result = Curl_auth_create_ntlm_type1_message(conn->user, conn->passwd,
334                                                      &conn->ntlm, &resp, &len);
335       }
336     else
337 #endif
338     if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) {
339       mech = SASL_MECH_STRING_OAUTHBEARER;
340       state1 = SASL_OAUTH2;
341       state2 = SASL_OAUTH2_RESP;
342       sasl->authused = SASL_MECH_OAUTHBEARER;
343
344       if(force_ir || data->set.sasl_ir)
345         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
346                                                        conn->host.name,
347                                                        conn->port,
348                                                        conn->oauth_bearer,
349                                                        &resp, &len);
350     }
351     else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) {
352       mech = SASL_MECH_STRING_XOAUTH2;
353       state1 = SASL_OAUTH2;
354       sasl->authused = SASL_MECH_XOAUTH2;
355
356       if(force_ir || data->set.sasl_ir)
357         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
358                                                        NULL, 0,
359                                                        conn->oauth_bearer,
360                                                        &resp, &len);
361     }
362     else if(enabledmechs & SASL_MECH_LOGIN) {
363       mech = SASL_MECH_STRING_LOGIN;
364       state1 = SASL_LOGIN;
365       state2 = SASL_LOGIN_PASSWD;
366       sasl->authused = SASL_MECH_LOGIN;
367
368       if(force_ir || data->set.sasl_ir)
369         result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
370     }
371     else if(enabledmechs & SASL_MECH_PLAIN) {
372       mech = SASL_MECH_STRING_PLAIN;
373       state1 = SASL_PLAIN;
374       sasl->authused = SASL_MECH_PLAIN;
375
376       if(force_ir || data->set.sasl_ir)
377         result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
378                                                 &resp, &len);
379     }
380   }
381
382   if(!result && mech) {
383     if(resp && sasl->params->maxirlen &&
384        strlen(mech) + len > sasl->params->maxirlen) {
385       free(resp);
386       resp = NULL;
387     }
388
389     result = sasl->params->sendauth(conn, mech, resp);
390     if(!result) {
391       *progress = SASL_INPROGRESS;
392       state(sasl, conn, resp ? state2 : state1);
393     }
394   }
395
396   free(resp);
397
398   return result;
399 }
400
401 /*
402  * Curl_sasl_continue()
403  *
404  * Continue the authentication.
405  */
406 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
407                             int code, saslprogress *progress)
408 {
409   CURLcode result = CURLE_OK;
410   struct Curl_easy *data = conn->data;
411   saslstate newstate = SASL_FINAL;
412   char *resp = NULL;
413 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
414   char *serverdata;
415   char *chlg = NULL;
416   size_t chlglen = 0;
417 #endif
418 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5)
419   const char *service = data->set.str[STRING_SERVICE_NAME] ?
420                         data->set.str[STRING_SERVICE_NAME] :
421                         sasl->params->service;
422 #endif
423   size_t len = 0;
424
425   *progress = SASL_INPROGRESS;
426
427   if(sasl->state == SASL_FINAL) {
428     if(code != sasl->params->finalcode)
429       result = CURLE_LOGIN_DENIED;
430     *progress = SASL_DONE;
431     state(sasl, conn, SASL_STOP);
432     return result;
433   }
434
435   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
436      code != sasl->params->contcode) {
437     *progress = SASL_DONE;
438     state(sasl, conn, SASL_STOP);
439     return CURLE_LOGIN_DENIED;
440   }
441
442   switch(sasl->state) {
443   case SASL_STOP:
444     *progress = SASL_DONE;
445     return result;
446   case SASL_PLAIN:
447     result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
448                                             &resp,
449                                             &len);
450     break;
451   case SASL_LOGIN:
452     result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
453     newstate = SASL_LOGIN_PASSWD;
454     break;
455   case SASL_LOGIN_PASSWD:
456     result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len);
457     break;
458   case SASL_EXTERNAL:
459     result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
460     break;
461
462 #ifndef CURL_DISABLE_CRYPTO_AUTH
463   case SASL_CRAMMD5:
464     sasl->params->getmessage(data->state.buffer, &serverdata);
465     result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen);
466     if(!result)
467       result = Curl_auth_create_cram_md5_message(data, chlg, conn->user,
468                                                  conn->passwd, &resp, &len);
469     free(chlg);
470     break;
471   case SASL_DIGESTMD5:
472     sasl->params->getmessage(data->state.buffer, &serverdata);
473     result = Curl_auth_create_digest_md5_message(data, serverdata,
474                                                  conn->user, conn->passwd,
475                                                  service,
476                                                  &resp, &len);
477     newstate = SASL_DIGESTMD5_RESP;
478     break;
479   case SASL_DIGESTMD5_RESP:
480     resp = strdup("");
481     if(!resp)
482       result = CURLE_OUT_OF_MEMORY;
483     break;
484 #endif
485
486 #ifdef USE_NTLM
487   case SASL_NTLM:
488     /* Create the type-1 message */
489     result = Curl_auth_create_ntlm_type1_message(conn->user, conn->passwd,
490                                                  &conn->ntlm, &resp, &len);
491     newstate = SASL_NTLM_TYPE2MSG;
492     break;
493   case SASL_NTLM_TYPE2MSG:
494     /* Decode the type-2 message */
495     sasl->params->getmessage(data->state.buffer, &serverdata);
496     result = Curl_auth_decode_ntlm_type2_message(data, serverdata,
497                                                  &conn->ntlm);
498     if(!result)
499       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
500                                                    conn->passwd, &conn->ntlm,
501                                                    &resp, &len);
502     break;
503 #endif
504
505 #if defined(USE_KERBEROS5)
506   case SASL_GSSAPI:
507     result = Curl_auth_create_gssapi_user_message(data, conn->user,
508                                                   conn->passwd,
509                                                   service,
510                                                   data->easy_conn->host.name,
511                                                   sasl->mutual_auth, NULL,
512                                                   &conn->krb5,
513                                                   &resp, &len);
514     newstate = SASL_GSSAPI_TOKEN;
515     break;
516   case SASL_GSSAPI_TOKEN:
517     sasl->params->getmessage(data->state.buffer, &serverdata);
518     if(sasl->mutual_auth) {
519       /* Decode the user token challenge and create the optional response
520          message */
521       result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
522                                                     NULL, NULL,
523                                                     sasl->mutual_auth,
524                                                     serverdata, &conn->krb5,
525                                                     &resp, &len);
526       newstate = SASL_GSSAPI_NO_DATA;
527     }
528     else
529       /* Decode the security challenge and create the response message */
530       result = Curl_auth_create_gssapi_security_message(data, serverdata,
531                                                         &conn->krb5,
532                                                         &resp, &len);
533     break;
534   case SASL_GSSAPI_NO_DATA:
535     sasl->params->getmessage(data->state.buffer, &serverdata);
536     /* Decode the security challenge and create the response message */
537     result = Curl_auth_create_gssapi_security_message(data, serverdata,
538                                                       &conn->krb5,
539                                                       &resp, &len);
540     break;
541 #endif
542
543   case SASL_OAUTH2:
544     /* Create the authorisation message */
545     if(sasl->authused == SASL_MECH_OAUTHBEARER) {
546       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
547                                                      conn->host.name,
548                                                      conn->port,
549                                                      conn->oauth_bearer,
550                                                      &resp, &len);
551
552       /* Failures maybe sent by the server as continuations for OAUTHBEARER */
553       newstate = SASL_OAUTH2_RESP;
554     }
555     else
556       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
557                                                      NULL, 0,
558                                                      conn->oauth_bearer,
559                                                      &resp, &len);
560     break;
561
562   case SASL_OAUTH2_RESP:
563     /* The continuation is optional so check the response code */
564     if(code == sasl->params->finalcode) {
565       /* Final response was received so we are done */
566       *progress = SASL_DONE;
567       state(sasl, conn, SASL_STOP);
568       return result;
569     }
570     else if(code == sasl->params->contcode) {
571       /* Acknowledge the continuation by sending a 0x01 response base64
572          encoded */
573       resp = strdup("AQ==");
574       if(!resp)
575         result = CURLE_OUT_OF_MEMORY;
576       break;
577     }
578     else {
579       *progress = SASL_DONE;
580       state(sasl, conn, SASL_STOP);
581       return CURLE_LOGIN_DENIED;
582     }
583
584   case SASL_CANCEL:
585     /* Remove the offending mechanism from the supported list */
586     sasl->authmechs ^= sasl->authused;
587
588     /* Start an alternative SASL authentication */
589     result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
590     newstate = sasl->state;   /* Use state from Curl_sasl_start() */
591     break;
592   default:
593     failf(data, "Unsupported SASL authentication mechanism");
594     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
595     break;
596   }
597
598   switch(result) {
599   case CURLE_BAD_CONTENT_ENCODING:
600     /* Cancel dialog */
601     result = sasl->params->sendcont(conn, "*");
602     newstate = SASL_CANCEL;
603     break;
604   case CURLE_OK:
605     if(resp)
606       result = sasl->params->sendcont(conn, resp);
607     break;
608   default:
609     newstate = SASL_STOP;    /* Stop on error */
610     *progress = SASL_DONE;
611     break;
612   }
613
614   free(resp);
615
616   state(sasl, conn, newstate);
617
618   return result;
619 }