• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2012 - 2019, 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 static 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  */
Curl_sasl_cleanup(struct connectdata * conn,unsigned int authused)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  */
Curl_sasl_decode_mech(const char * ptr,size_t maxlen,size_t * len)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  */
Curl_sasl_parse_url_auth_option(struct SASL * sasl,const char * value,size_t len)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   size_t mechlen;
150 
151   if(!len)
152     return CURLE_URL_MALFORMAT;
153 
154   if(sasl->resetprefs) {
155     sasl->resetprefs = FALSE;
156     sasl->prefmech = SASL_AUTH_NONE;
157   }
158 
159   if(!strncmp(value, "*", len))
160     sasl->prefmech = SASL_AUTH_DEFAULT;
161   else {
162     unsigned int mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
163     if(mechbit && mechlen == len)
164       sasl->prefmech |= mechbit;
165     else
166       result = CURLE_URL_MALFORMAT;
167   }
168 
169   return result;
170 }
171 
172 /*
173  * Curl_sasl_init()
174  *
175  * Initializes the SASL structure.
176  */
Curl_sasl_init(struct SASL * sasl,const struct SASLproto * params)177 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
178 {
179   sasl->params = params;           /* Set protocol dependent parameters */
180   sasl->state = SASL_STOP;         /* Not yet running */
181   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
182   sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
183   sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
184   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
185   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
186   sasl->force_ir = FALSE;          /* Respect external option */
187 }
188 
189 /*
190  * state()
191  *
192  * This is the ONLY way to change SASL state!
193  */
state(struct SASL * sasl,struct connectdata * conn,saslstate newstate)194 static void state(struct SASL *sasl, struct connectdata *conn,
195                   saslstate newstate)
196 {
197 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
198   /* for debug purposes */
199   static const char * const names[]={
200     "STOP",
201     "PLAIN",
202     "LOGIN",
203     "LOGIN_PASSWD",
204     "EXTERNAL",
205     "CRAMMD5",
206     "DIGESTMD5",
207     "DIGESTMD5_RESP",
208     "NTLM",
209     "NTLM_TYPE2MSG",
210     "GSSAPI",
211     "GSSAPI_TOKEN",
212     "GSSAPI_NO_DATA",
213     "OAUTH2",
214     "OAUTH2_RESP",
215     "CANCEL",
216     "FINAL",
217     /* LAST */
218   };
219 
220   if(sasl->state != newstate)
221     infof(conn->data, "SASL %p state change from %s to %s\n",
222           (void *)sasl, names[sasl->state], names[newstate]);
223 #else
224   (void) conn;
225 #endif
226 
227   sasl->state = newstate;
228 }
229 
230 /*
231  * Curl_sasl_can_authenticate()
232  *
233  * Check if we have enough auth data and capabilities to authenticate.
234  */
Curl_sasl_can_authenticate(struct SASL * sasl,struct connectdata * conn)235 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
236 {
237   /* Have credentials been provided? */
238   if(conn->bits.user_passwd)
239     return TRUE;
240 
241   /* EXTERNAL can authenticate without a user name and/or password */
242   if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
243     return TRUE;
244 
245   return FALSE;
246 }
247 
248 /*
249  * Curl_sasl_start()
250  *
251  * Calculate the required login details for SASL authentication.
252  */
Curl_sasl_start(struct SASL * sasl,struct connectdata * conn,bool force_ir,saslprogress * progress)253 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
254                          bool force_ir, saslprogress *progress)
255 {
256   CURLcode result = CURLE_OK;
257   struct Curl_easy *data = conn->data;
258   unsigned int enabledmechs;
259   const char *mech = NULL;
260   char *resp = NULL;
261   size_t len = 0;
262   saslstate state1 = SASL_STOP;
263   saslstate state2 = SASL_FINAL;
264   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
265     conn->host.name;
266   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
267 #if defined(USE_KERBEROS5) || defined(USE_NTLM)
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->conn->host.name,
304                                                       sasl->mutual_auth,
305                                                       NULL, &conn->krb5,
306                                                       &resp, &len);
307     }
308     else
309 #endif
310 #ifndef CURL_DISABLE_CRYPTO_AUTH
311     if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
312        Curl_auth_is_digest_supported()) {
313       mech = SASL_MECH_STRING_DIGEST_MD5;
314       state1 = SASL_DIGESTMD5;
315       sasl->authused = SASL_MECH_DIGEST_MD5;
316     }
317     else if(enabledmechs & SASL_MECH_CRAM_MD5) {
318       mech = SASL_MECH_STRING_CRAM_MD5;
319       state1 = SASL_CRAMMD5;
320       sasl->authused = SASL_MECH_CRAM_MD5;
321     }
322     else
323 #endif
324 #ifdef USE_NTLM
325     if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
326       mech = SASL_MECH_STRING_NTLM;
327       state1 = SASL_NTLM;
328       state2 = SASL_NTLM_TYPE2MSG;
329       sasl->authused = SASL_MECH_NTLM;
330 
331       if(force_ir || data->set.sasl_ir)
332         result = Curl_auth_create_ntlm_type1_message(data,
333                                                      conn->user, conn->passwd,
334                                                      service,
335                                                      hostname,
336                                                      &conn->ntlm, &resp,
337                                                      &len);
338       }
339     else
340 #endif
341     if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) {
342       mech = SASL_MECH_STRING_OAUTHBEARER;
343       state1 = SASL_OAUTH2;
344       state2 = SASL_OAUTH2_RESP;
345       sasl->authused = SASL_MECH_OAUTHBEARER;
346 
347       if(force_ir || data->set.sasl_ir)
348         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
349                                                        hostname,
350                                                        port,
351                                                        conn->oauth_bearer,
352                                                        &resp, &len);
353     }
354     else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) {
355       mech = SASL_MECH_STRING_XOAUTH2;
356       state1 = SASL_OAUTH2;
357       sasl->authused = SASL_MECH_XOAUTH2;
358 
359       if(force_ir || data->set.sasl_ir)
360         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
361                                                        NULL, 0,
362                                                        conn->oauth_bearer,
363                                                        &resp, &len);
364     }
365     else if(enabledmechs & SASL_MECH_PLAIN) {
366       mech = SASL_MECH_STRING_PLAIN;
367       state1 = SASL_PLAIN;
368       sasl->authused = SASL_MECH_PLAIN;
369 
370       if(force_ir || data->set.sasl_ir)
371         result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
372                                                 &resp, &len);
373     }
374     else if(enabledmechs & SASL_MECH_LOGIN) {
375       mech = SASL_MECH_STRING_LOGIN;
376       state1 = SASL_LOGIN;
377       state2 = SASL_LOGIN_PASSWD;
378       sasl->authused = SASL_MECH_LOGIN;
379 
380       if(force_ir || data->set.sasl_ir)
381         result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
382     }
383   }
384 
385   if(!result && mech) {
386     if(resp && sasl->params->maxirlen &&
387        strlen(mech) + len > sasl->params->maxirlen) {
388       free(resp);
389       resp = NULL;
390     }
391 
392     result = sasl->params->sendauth(conn, mech, resp);
393     if(!result) {
394       *progress = SASL_INPROGRESS;
395       state(sasl, conn, resp ? state2 : state1);
396     }
397   }
398 
399   free(resp);
400 
401   return result;
402 }
403 
404 /*
405  * Curl_sasl_continue()
406  *
407  * Continue the authentication.
408  */
Curl_sasl_continue(struct SASL * sasl,struct connectdata * conn,int code,saslprogress * progress)409 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
410                             int code, saslprogress *progress)
411 {
412   CURLcode result = CURLE_OK;
413   struct Curl_easy *data = conn->data;
414   saslstate newstate = SASL_FINAL;
415   char *resp = NULL;
416   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
417     conn->host.name;
418   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
419 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
420   char *chlg = NULL;
421   size_t chlglen = 0;
422 #endif
423 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) || \
424     defined(USE_NTLM)
425   const char *service = data->set.str[STRING_SERVICE_NAME] ?
426                         data->set.str[STRING_SERVICE_NAME] :
427                         sasl->params->service;
428   char *serverdata;
429 #endif
430   size_t len = 0;
431 
432   *progress = SASL_INPROGRESS;
433 
434   if(sasl->state == SASL_FINAL) {
435     if(code != sasl->params->finalcode)
436       result = CURLE_LOGIN_DENIED;
437     *progress = SASL_DONE;
438     state(sasl, conn, SASL_STOP);
439     return result;
440   }
441 
442   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
443      code != sasl->params->contcode) {
444     *progress = SASL_DONE;
445     state(sasl, conn, SASL_STOP);
446     return CURLE_LOGIN_DENIED;
447   }
448 
449   switch(sasl->state) {
450   case SASL_STOP:
451     *progress = SASL_DONE;
452     return result;
453   case SASL_PLAIN:
454     result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
455                                             &resp,
456                                             &len);
457     break;
458   case SASL_LOGIN:
459     result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
460     newstate = SASL_LOGIN_PASSWD;
461     break;
462   case SASL_LOGIN_PASSWD:
463     result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len);
464     break;
465   case SASL_EXTERNAL:
466     result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
467     break;
468 
469 #ifndef CURL_DISABLE_CRYPTO_AUTH
470   case SASL_CRAMMD5:
471     sasl->params->getmessage(data->state.buffer, &serverdata);
472     result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen);
473     if(!result)
474       result = Curl_auth_create_cram_md5_message(data, chlg, conn->user,
475                                                  conn->passwd, &resp, &len);
476     free(chlg);
477     break;
478   case SASL_DIGESTMD5:
479     sasl->params->getmessage(data->state.buffer, &serverdata);
480     result = Curl_auth_create_digest_md5_message(data, serverdata,
481                                                  conn->user, conn->passwd,
482                                                  service,
483                                                  &resp, &len);
484     newstate = SASL_DIGESTMD5_RESP;
485     break;
486   case SASL_DIGESTMD5_RESP:
487     resp = strdup("");
488     if(!resp)
489       result = CURLE_OUT_OF_MEMORY;
490     break;
491 #endif
492 
493 #ifdef USE_NTLM
494   case SASL_NTLM:
495     /* Create the type-1 message */
496     result = Curl_auth_create_ntlm_type1_message(data,
497                                                  conn->user, conn->passwd,
498                                                  service, hostname,
499                                                  &conn->ntlm, &resp, &len);
500     newstate = SASL_NTLM_TYPE2MSG;
501     break;
502   case SASL_NTLM_TYPE2MSG:
503     /* Decode the type-2 message */
504     sasl->params->getmessage(data->state.buffer, &serverdata);
505     result = Curl_auth_decode_ntlm_type2_message(data, serverdata,
506                                                  &conn->ntlm);
507     if(!result)
508       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
509                                                    conn->passwd, &conn->ntlm,
510                                                    &resp, &len);
511     break;
512 #endif
513 
514 #if defined(USE_KERBEROS5)
515   case SASL_GSSAPI:
516     result = Curl_auth_create_gssapi_user_message(data, conn->user,
517                                                   conn->passwd,
518                                                   service,
519                                                   data->conn->host.name,
520                                                   sasl->mutual_auth, NULL,
521                                                   &conn->krb5,
522                                                   &resp, &len);
523     newstate = SASL_GSSAPI_TOKEN;
524     break;
525   case SASL_GSSAPI_TOKEN:
526     sasl->params->getmessage(data->state.buffer, &serverdata);
527     if(sasl->mutual_auth) {
528       /* Decode the user token challenge and create the optional response
529          message */
530       result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
531                                                     NULL, NULL,
532                                                     sasl->mutual_auth,
533                                                     serverdata, &conn->krb5,
534                                                     &resp, &len);
535       newstate = SASL_GSSAPI_NO_DATA;
536     }
537     else
538       /* Decode the security challenge and create the response message */
539       result = Curl_auth_create_gssapi_security_message(data, serverdata,
540                                                         &conn->krb5,
541                                                         &resp, &len);
542     break;
543   case SASL_GSSAPI_NO_DATA:
544     sasl->params->getmessage(data->state.buffer, &serverdata);
545     /* Decode the security challenge and create the response message */
546     result = Curl_auth_create_gssapi_security_message(data, serverdata,
547                                                       &conn->krb5,
548                                                       &resp, &len);
549     break;
550 #endif
551 
552   case SASL_OAUTH2:
553     /* Create the authorisation message */
554     if(sasl->authused == SASL_MECH_OAUTHBEARER) {
555       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
556                                                      hostname,
557                                                      port,
558                                                      conn->oauth_bearer,
559                                                      &resp, &len);
560 
561       /* Failures maybe sent by the server as continuations for OAUTHBEARER */
562       newstate = SASL_OAUTH2_RESP;
563     }
564     else
565       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
566                                                      NULL, 0,
567                                                      conn->oauth_bearer,
568                                                      &resp, &len);
569     break;
570 
571   case SASL_OAUTH2_RESP:
572     /* The continuation is optional so check the response code */
573     if(code == sasl->params->finalcode) {
574       /* Final response was received so we are done */
575       *progress = SASL_DONE;
576       state(sasl, conn, SASL_STOP);
577       return result;
578     }
579     else if(code == sasl->params->contcode) {
580       /* Acknowledge the continuation by sending a 0x01 response base64
581          encoded */
582       resp = strdup("AQ==");
583       if(!resp)
584         result = CURLE_OUT_OF_MEMORY;
585       break;
586     }
587     else {
588       *progress = SASL_DONE;
589       state(sasl, conn, SASL_STOP);
590       return CURLE_LOGIN_DENIED;
591     }
592 
593   case SASL_CANCEL:
594     /* Remove the offending mechanism from the supported list */
595     sasl->authmechs ^= sasl->authused;
596 
597     /* Start an alternative SASL authentication */
598     result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
599     newstate = sasl->state;   /* Use state from Curl_sasl_start() */
600     break;
601   default:
602     failf(data, "Unsupported SASL authentication mechanism");
603     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
604     break;
605   }
606 
607   switch(result) {
608   case CURLE_BAD_CONTENT_ENCODING:
609     /* Cancel dialog */
610     result = sasl->params->sendcont(conn, "*");
611     newstate = SASL_CANCEL;
612     break;
613   case CURLE_OK:
614     if(resp)
615       result = sasl->params->sendcont(conn, resp);
616     break;
617   default:
618     newstate = SASL_STOP;    /* Stop on error */
619     *progress = SASL_DONE;
620     break;
621   }
622 
623   free(resp);
624 
625   state(sasl, conn, newstate);
626 
627   return result;
628 }
629