curl_sasl.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC2195 CRAM-MD5 authentication
00022  * RFC2617 Basic and Digest Access Authentication
00023  * RFC2831 DIGEST-MD5 authentication
00024  * RFC4422 Simple Authentication and Security Layer (SASL)
00025  * RFC4616 PLAIN authentication
00026  * RFC6749 OAuth 2.0 Authorization Framework
00027  * RFC7628 A Set of SASL Mechanisms for OAuth
00028  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
00029  *
00030  ***************************************************************************/
00031 
00032 #include "curl_setup.h"
00033 
00034 #include <curl/curl.h>
00035 #include "urldata.h"
00036 
00037 #include "curl_base64.h"
00038 #include "curl_md5.h"
00039 #include "vauth/vauth.h"
00040 #include "vtls/vtls.h"
00041 #include "curl_hmac.h"
00042 #include "curl_sasl.h"
00043 #include "warnless.h"
00044 #include "strtok.h"
00045 #include "sendf.h"
00046 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
00047 /* The last 3 #include files should be in this order */
00048 #include "curl_printf.h"
00049 #include "curl_memory.h"
00050 #include "memdebug.h"
00051 
00052 /* Supported mechanisms */
00053 const struct {
00054   const char   *name;  /* Name */
00055   size_t        len;   /* Name length */
00056   unsigned int  bit;   /* Flag bit */
00057 } mechtable[] = {
00058   { "LOGIN",        5,  SASL_MECH_LOGIN },
00059   { "PLAIN",        5,  SASL_MECH_PLAIN },
00060   { "CRAM-MD5",     8,  SASL_MECH_CRAM_MD5 },
00061   { "DIGEST-MD5",   10, SASL_MECH_DIGEST_MD5 },
00062   { "GSSAPI",       6,  SASL_MECH_GSSAPI },
00063   { "EXTERNAL",     8,  SASL_MECH_EXTERNAL },
00064   { "NTLM",         4,  SASL_MECH_NTLM },
00065   { "XOAUTH2",      7,  SASL_MECH_XOAUTH2 },
00066   { "OAUTHBEARER",  11, SASL_MECH_OAUTHBEARER },
00067   { ZERO_NULL,      0,  0 }
00068 };
00069 
00070 /*
00071  * Curl_sasl_cleanup()
00072  *
00073  * This is used to cleanup any libraries or curl modules used by the sasl
00074  * functions.
00075  *
00076  * Parameters:
00077  *
00078  * conn     [in]     - The connection data.
00079  * authused [in]     - The authentication mechanism used.
00080  */
00081 void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
00082 {
00083 #if defined(USE_KERBEROS5)
00084   /* Cleanup the gssapi structure */
00085   if(authused == SASL_MECH_GSSAPI) {
00086     Curl_auth_gssapi_cleanup(&conn->krb5);
00087   }
00088 #endif
00089 
00090 #if defined(USE_NTLM)
00091   /* Cleanup the NTLM structure */
00092   if(authused == SASL_MECH_NTLM) {
00093     Curl_auth_ntlm_cleanup(&conn->ntlm);
00094   }
00095 #endif
00096 
00097 #if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
00098   /* Reserved for future use */
00099   (void)conn;
00100   (void)authused;
00101 #endif
00102 }
00103 
00104 /*
00105  * Curl_sasl_decode_mech()
00106  *
00107  * Convert a SASL mechanism name into a token.
00108  *
00109  * Parameters:
00110  *
00111  * ptr    [in]     - The mechanism string.
00112  * maxlen [in]     - Maximum mechanism string length.
00113  * len    [out]    - If not NULL, effective name length.
00114  *
00115  * Returns the SASL mechanism token or 0 if no match.
00116  */
00117 unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
00118 {
00119   unsigned int i;
00120   char c;
00121 
00122   for(i = 0; mechtable[i].name; i++) {
00123     if(maxlen >= mechtable[i].len &&
00124        !memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
00125       if(len)
00126         *len = mechtable[i].len;
00127 
00128       if(maxlen == mechtable[i].len)
00129         return mechtable[i].bit;
00130 
00131       c = ptr[mechtable[i].len];
00132       if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
00133         return mechtable[i].bit;
00134     }
00135   }
00136 
00137   return 0;
00138 }
00139 
00140 /*
00141  * Curl_sasl_parse_url_auth_option()
00142  *
00143  * Parse the URL login options.
00144  */
00145 CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
00146                                          const char *value, size_t len)
00147 {
00148   CURLcode result = CURLE_OK;
00149   unsigned int mechbit;
00150   size_t mechlen;
00151 
00152   if(!len)
00153     return CURLE_URL_MALFORMAT;
00154 
00155   if(sasl->resetprefs) {
00156     sasl->resetprefs = FALSE;
00157     sasl->prefmech = SASL_AUTH_NONE;
00158   }
00159 
00160   if(!strncmp(value, "*", len))
00161     sasl->prefmech = SASL_AUTH_DEFAULT;
00162   else {
00163     mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
00164     if(mechbit && mechlen == len)
00165       sasl->prefmech |= mechbit;
00166     else
00167       result = CURLE_URL_MALFORMAT;
00168   }
00169 
00170   return result;
00171 }
00172 
00173 /*
00174  * Curl_sasl_init()
00175  *
00176  * Initializes the SASL structure.
00177  */
00178 void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
00179 {
00180   sasl->params = params;           /* Set protocol dependent parameters */
00181   sasl->state = SASL_STOP;         /* Not yet running */
00182   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
00183   sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
00184   sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
00185   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
00186   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
00187   sasl->force_ir = FALSE;          /* Respect external option */
00188 }
00189 
00190 /*
00191  * state()
00192  *
00193  * This is the ONLY way to change SASL state!
00194  */
00195 static void state(struct SASL *sasl, struct connectdata *conn,
00196                   saslstate newstate)
00197 {
00198 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00199   /* for debug purposes */
00200   static const char * const names[]={
00201     "STOP",
00202     "PLAIN",
00203     "LOGIN",
00204     "LOGIN_PASSWD",
00205     "EXTERNAL",
00206     "CRAMMD5",
00207     "DIGESTMD5",
00208     "DIGESTMD5_RESP",
00209     "NTLM",
00210     "NTLM_TYPE2MSG",
00211     "GSSAPI",
00212     "GSSAPI_TOKEN",
00213     "GSSAPI_NO_DATA",
00214     "OAUTH2",
00215     "OAUTH2_RESP",
00216     "CANCEL",
00217     "FINAL",
00218     /* LAST */
00219   };
00220 
00221   if(sasl->state != newstate)
00222     infof(conn->data, "SASL %p state change from %s to %s\n",
00223           (void *)sasl, names[sasl->state], names[newstate]);
00224 #else
00225   (void) conn;
00226 #endif
00227 
00228   sasl->state = newstate;
00229 }
00230 
00231 /*
00232  * Curl_sasl_can_authenticate()
00233  *
00234  * Check if we have enough auth data and capabilities to authenticate.
00235  */
00236 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
00237 {
00238   /* Have credentials been provided? */
00239   if(conn->bits.user_passwd)
00240     return TRUE;
00241 
00242   /* EXTERNAL can authenticate without a user name and/or password */
00243   if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
00244     return TRUE;
00245 
00246   return FALSE;
00247 }
00248 
00249 /*
00250  * Curl_sasl_start()
00251  *
00252  * Calculate the required login details for SASL authentication.
00253  */
00254 CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
00255                          bool force_ir, saslprogress *progress)
00256 {
00257   CURLcode result = CURLE_OK;
00258   struct Curl_easy *data = conn->data;
00259   unsigned int enabledmechs;
00260   const char *mech = NULL;
00261   char *resp = NULL;
00262   size_t len = 0;
00263   saslstate state1 = SASL_STOP;
00264   saslstate state2 = SASL_FINAL;
00265   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
00266     conn->host.name;
00267   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
00268 #if defined(USE_KERBEROS5)
00269   const char *service = data->set.str[STRING_SERVICE_NAME] ?
00270     data->set.str[STRING_SERVICE_NAME] :
00271     sasl->params->service;
00272 #endif
00273 
00274   sasl->force_ir = force_ir;    /* Latch for future use */
00275   sasl->authused = 0;           /* No mechanism used yet */
00276   enabledmechs = sasl->authmechs & sasl->prefmech;
00277   *progress = SASL_IDLE;
00278 
00279   /* Calculate the supported authentication mechanism, by decreasing order of
00280      security, as well as the initial response where appropriate */
00281   if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
00282     mech = SASL_MECH_STRING_EXTERNAL;
00283     state1 = SASL_EXTERNAL;
00284     sasl->authused = SASL_MECH_EXTERNAL;
00285 
00286     if(force_ir || data->set.sasl_ir)
00287       result = Curl_auth_create_external_message(data, conn->user, &resp,
00288                                                  &len);
00289   }
00290   else if(conn->bits.user_passwd) {
00291 #if defined(USE_KERBEROS5)
00292     if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
00293        Curl_auth_user_contains_domain(conn->user)) {
00294       sasl->mutual_auth = FALSE; /* TODO: Calculate mutual authentication */
00295       mech = SASL_MECH_STRING_GSSAPI;
00296       state1 = SASL_GSSAPI;
00297       state2 = SASL_GSSAPI_TOKEN;
00298       sasl->authused = SASL_MECH_GSSAPI;
00299 
00300       if(force_ir || data->set.sasl_ir)
00301         result = Curl_auth_create_gssapi_user_message(data, conn->user,
00302                                                       conn->passwd,
00303                                                       service,
00304                                                       data->easy_conn->
00305                                                             host.name,
00306                                                       sasl->mutual_auth,
00307                                                       NULL, &conn->krb5,
00308                                                       &resp, &len);
00309     }
00310     else
00311 #endif
00312 #ifndef CURL_DISABLE_CRYPTO_AUTH
00313     if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
00314        Curl_auth_is_digest_supported()) {
00315       mech = SASL_MECH_STRING_DIGEST_MD5;
00316       state1 = SASL_DIGESTMD5;
00317       sasl->authused = SASL_MECH_DIGEST_MD5;
00318     }
00319     else if(enabledmechs & SASL_MECH_CRAM_MD5) {
00320       mech = SASL_MECH_STRING_CRAM_MD5;
00321       state1 = SASL_CRAMMD5;
00322       sasl->authused = SASL_MECH_CRAM_MD5;
00323     }
00324     else
00325 #endif
00326 #ifdef USE_NTLM
00327     if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
00328       mech = SASL_MECH_STRING_NTLM;
00329       state1 = SASL_NTLM;
00330       state2 = SASL_NTLM_TYPE2MSG;
00331       sasl->authused = SASL_MECH_NTLM;
00332 
00333       if(force_ir || data->set.sasl_ir)
00334         result = Curl_auth_create_ntlm_type1_message(conn->user, conn->passwd,
00335                                                      &conn->ntlm, &resp, &len);
00336       }
00337     else
00338 #endif
00339     if((enabledmechs & SASL_MECH_OAUTHBEARER) && conn->oauth_bearer) {
00340       mech = SASL_MECH_STRING_OAUTHBEARER;
00341       state1 = SASL_OAUTH2;
00342       state2 = SASL_OAUTH2_RESP;
00343       sasl->authused = SASL_MECH_OAUTHBEARER;
00344 
00345       if(force_ir || data->set.sasl_ir)
00346         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
00347                                                        hostname,
00348                                                        port,
00349                                                        conn->oauth_bearer,
00350                                                        &resp, &len);
00351     }
00352     else if((enabledmechs & SASL_MECH_XOAUTH2) && conn->oauth_bearer) {
00353       mech = SASL_MECH_STRING_XOAUTH2;
00354       state1 = SASL_OAUTH2;
00355       sasl->authused = SASL_MECH_XOAUTH2;
00356 
00357       if(force_ir || data->set.sasl_ir)
00358         result = Curl_auth_create_oauth_bearer_message(data, conn->user,
00359                                                        NULL, 0,
00360                                                        conn->oauth_bearer,
00361                                                        &resp, &len);
00362     }
00363     else if(enabledmechs & SASL_MECH_LOGIN) {
00364       mech = SASL_MECH_STRING_LOGIN;
00365       state1 = SASL_LOGIN;
00366       state2 = SASL_LOGIN_PASSWD;
00367       sasl->authused = SASL_MECH_LOGIN;
00368 
00369       if(force_ir || data->set.sasl_ir)
00370         result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
00371     }
00372     else if(enabledmechs & SASL_MECH_PLAIN) {
00373       mech = SASL_MECH_STRING_PLAIN;
00374       state1 = SASL_PLAIN;
00375       sasl->authused = SASL_MECH_PLAIN;
00376 
00377       if(force_ir || data->set.sasl_ir)
00378         result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
00379                                                 &resp, &len);
00380     }
00381   }
00382 
00383   if(!result && mech) {
00384     if(resp && sasl->params->maxirlen &&
00385        strlen(mech) + len > sasl->params->maxirlen) {
00386       free(resp);
00387       resp = NULL;
00388     }
00389 
00390     result = sasl->params->sendauth(conn, mech, resp);
00391     if(!result) {
00392       *progress = SASL_INPROGRESS;
00393       state(sasl, conn, resp ? state2 : state1);
00394     }
00395   }
00396 
00397   free(resp);
00398 
00399   return result;
00400 }
00401 
00402 /*
00403  * Curl_sasl_continue()
00404  *
00405  * Continue the authentication.
00406  */
00407 CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
00408                             int code, saslprogress *progress)
00409 {
00410   CURLcode result = CURLE_OK;
00411   struct Curl_easy *data = conn->data;
00412   saslstate newstate = SASL_FINAL;
00413   char *resp = NULL;
00414   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
00415     conn->host.name;
00416   const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
00417 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
00418   char *serverdata;
00419   char *chlg = NULL;
00420   size_t chlglen = 0;
00421 #endif
00422 #if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5)
00423   const char *service = data->set.str[STRING_SERVICE_NAME] ?
00424                         data->set.str[STRING_SERVICE_NAME] :
00425                         sasl->params->service;
00426 #endif
00427   size_t len = 0;
00428 
00429   *progress = SASL_INPROGRESS;
00430 
00431   if(sasl->state == SASL_FINAL) {
00432     if(code != sasl->params->finalcode)
00433       result = CURLE_LOGIN_DENIED;
00434     *progress = SASL_DONE;
00435     state(sasl, conn, SASL_STOP);
00436     return result;
00437   }
00438 
00439   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
00440      code != sasl->params->contcode) {
00441     *progress = SASL_DONE;
00442     state(sasl, conn, SASL_STOP);
00443     return CURLE_LOGIN_DENIED;
00444   }
00445 
00446   switch(sasl->state) {
00447   case SASL_STOP:
00448     *progress = SASL_DONE;
00449     return result;
00450   case SASL_PLAIN:
00451     result = Curl_auth_create_plain_message(data, conn->user, conn->passwd,
00452                                             &resp,
00453                                             &len);
00454     break;
00455   case SASL_LOGIN:
00456     result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
00457     newstate = SASL_LOGIN_PASSWD;
00458     break;
00459   case SASL_LOGIN_PASSWD:
00460     result = Curl_auth_create_login_message(data, conn->passwd, &resp, &len);
00461     break;
00462   case SASL_EXTERNAL:
00463     result = Curl_auth_create_external_message(data, conn->user, &resp, &len);
00464     break;
00465 
00466 #ifndef CURL_DISABLE_CRYPTO_AUTH
00467   case SASL_CRAMMD5:
00468     sasl->params->getmessage(data->state.buffer, &serverdata);
00469     result = Curl_auth_decode_cram_md5_message(serverdata, &chlg, &chlglen);
00470     if(!result)
00471       result = Curl_auth_create_cram_md5_message(data, chlg, conn->user,
00472                                                  conn->passwd, &resp, &len);
00473     free(chlg);
00474     break;
00475   case SASL_DIGESTMD5:
00476     sasl->params->getmessage(data->state.buffer, &serverdata);
00477     result = Curl_auth_create_digest_md5_message(data, serverdata,
00478                                                  conn->user, conn->passwd,
00479                                                  service,
00480                                                  &resp, &len);
00481     newstate = SASL_DIGESTMD5_RESP;
00482     break;
00483   case SASL_DIGESTMD5_RESP:
00484     resp = strdup("");
00485     if(!resp)
00486       result = CURLE_OUT_OF_MEMORY;
00487     break;
00488 #endif
00489 
00490 #ifdef USE_NTLM
00491   case SASL_NTLM:
00492     /* Create the type-1 message */
00493     result = Curl_auth_create_ntlm_type1_message(conn->user, conn->passwd,
00494                                                  &conn->ntlm, &resp, &len);
00495     newstate = SASL_NTLM_TYPE2MSG;
00496     break;
00497   case SASL_NTLM_TYPE2MSG:
00498     /* Decode the type-2 message */
00499     sasl->params->getmessage(data->state.buffer, &serverdata);
00500     result = Curl_auth_decode_ntlm_type2_message(data, serverdata,
00501                                                  &conn->ntlm);
00502     if(!result)
00503       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
00504                                                    conn->passwd, &conn->ntlm,
00505                                                    &resp, &len);
00506     break;
00507 #endif
00508 
00509 #if defined(USE_KERBEROS5)
00510   case SASL_GSSAPI:
00511     result = Curl_auth_create_gssapi_user_message(data, conn->user,
00512                                                   conn->passwd,
00513                                                   service,
00514                                                   data->easy_conn->host.name,
00515                                                   sasl->mutual_auth, NULL,
00516                                                   &conn->krb5,
00517                                                   &resp, &len);
00518     newstate = SASL_GSSAPI_TOKEN;
00519     break;
00520   case SASL_GSSAPI_TOKEN:
00521     sasl->params->getmessage(data->state.buffer, &serverdata);
00522     if(sasl->mutual_auth) {
00523       /* Decode the user token challenge and create the optional response
00524          message */
00525       result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
00526                                                     NULL, NULL,
00527                                                     sasl->mutual_auth,
00528                                                     serverdata, &conn->krb5,
00529                                                     &resp, &len);
00530       newstate = SASL_GSSAPI_NO_DATA;
00531     }
00532     else
00533       /* Decode the security challenge and create the response message */
00534       result = Curl_auth_create_gssapi_security_message(data, serverdata,
00535                                                         &conn->krb5,
00536                                                         &resp, &len);
00537     break;
00538   case SASL_GSSAPI_NO_DATA:
00539     sasl->params->getmessage(data->state.buffer, &serverdata);
00540     /* Decode the security challenge and create the response message */
00541     result = Curl_auth_create_gssapi_security_message(data, serverdata,
00542                                                       &conn->krb5,
00543                                                       &resp, &len);
00544     break;
00545 #endif
00546 
00547   case SASL_OAUTH2:
00548     /* Create the authorisation message */
00549     if(sasl->authused == SASL_MECH_OAUTHBEARER) {
00550       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
00551                                                      hostname,
00552                                                      port,
00553                                                      conn->oauth_bearer,
00554                                                      &resp, &len);
00555 
00556       /* Failures maybe sent by the server as continuations for OAUTHBEARER */
00557       newstate = SASL_OAUTH2_RESP;
00558     }
00559     else
00560       result = Curl_auth_create_oauth_bearer_message(data, conn->user,
00561                                                      NULL, 0,
00562                                                      conn->oauth_bearer,
00563                                                      &resp, &len);
00564     break;
00565 
00566   case SASL_OAUTH2_RESP:
00567     /* The continuation is optional so check the response code */
00568     if(code == sasl->params->finalcode) {
00569       /* Final response was received so we are done */
00570       *progress = SASL_DONE;
00571       state(sasl, conn, SASL_STOP);
00572       return result;
00573     }
00574     else if(code == sasl->params->contcode) {
00575       /* Acknowledge the continuation by sending a 0x01 response base64
00576          encoded */
00577       resp = strdup("AQ==");
00578       if(!resp)
00579         result = CURLE_OUT_OF_MEMORY;
00580       break;
00581     }
00582     else {
00583       *progress = SASL_DONE;
00584       state(sasl, conn, SASL_STOP);
00585       return CURLE_LOGIN_DENIED;
00586     }
00587 
00588   case SASL_CANCEL:
00589     /* Remove the offending mechanism from the supported list */
00590     sasl->authmechs ^= sasl->authused;
00591 
00592     /* Start an alternative SASL authentication */
00593     result = Curl_sasl_start(sasl, conn, sasl->force_ir, progress);
00594     newstate = sasl->state;   /* Use state from Curl_sasl_start() */
00595     break;
00596   default:
00597     failf(data, "Unsupported SASL authentication mechanism");
00598     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
00599     break;
00600   }
00601 
00602   switch(result) {
00603   case CURLE_BAD_CONTENT_ENCODING:
00604     /* Cancel dialog */
00605     result = sasl->params->sendcont(conn, "*");
00606     newstate = SASL_CANCEL;
00607     break;
00608   case CURLE_OK:
00609     if(resp)
00610       result = sasl->params->sendcont(conn, resp);
00611     break;
00612   default:
00613     newstate = SASL_STOP;    /* Stop on error */
00614     *progress = SASL_DONE;
00615     break;
00616   }
00617 
00618   free(resp);
00619 
00620   state(sasl, conn, newstate);
00621 
00622   return result;
00623 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02