#include "psauth.h" #include #include //used for strcpy, etc. #include //used for malloc #include #include #include #include #include #include #include #include #include #include #include #include "sasl.h" #include "saslutil.h" #include "key.h" #define kBigBuffSize 4096 #define kSASLListPrefix "(SASL " #define kPasswordServerErrPrefixStr "-ERR " #define kPasswordServerSASLErrPrefixStr "SASL " typedef enum PWServerErrorType { kPolicyError, kSASLError } PWServerErrorType; typedef struct PWServerError { int err; PWServerErrorType type; } PWServerError; int getnameinfo(const struct sockaddr *, socklen_t, char *, size_t, char *, size_t, int); typedef int sasl_cbproc(); int getrealm(void *context /*__attribute__((unused))*/, int id, const char **availrealms, const char **result); int simple(void *context /*__attribute__((unused))*/, int id, const char **result, unsigned *len); int getsecret(sasl_conn_t *conn, void *context /*__attribute__((unused))*/, int id, sasl_secret_t **psecret); typedef struct AuthMethName { char method[SASL_MECHNAMEMAX + 1]; } AuthMethName; // Context data structure typedef struct sPSContextData { // char *psName; //domain or ip address of passwordserver int offset; //offset for GetDirNodeInfo data extraction // TODO ... more as needed sasl_conn_t *conn; FILE *serverIn, *serverOut; int fd; AuthMethName *mech; int mechCount; char *rsaPublicKeyStr; Key *rsaPublicKey; char username[35]; const char *password; } sPSContextData; int DoRSAValidation ( sPSContextData *inContext, const char *inUserKey ); void writeToServer( FILE *out, char *buf ); void writeToServer( FILE *out, char *buf ) { fwrite(buf, strlen(buf), 1, out); fflush(out); } PWServerError readFromServer( FILE *in, char *buf, unsigned long bufLen ); PWServerError readFromServer( FILE *in, char *buf, unsigned long bufLen ) { char readChar; char *tstr = buf; PWServerError result = {0, kPolicyError}; int compareLen; if ( buf == NULL || bufLen < 2 ) { result.err = -1; return result; } *buf = '\0'; do { fscanf( in, "%c", &readChar ); #ifdef DEBUG_PRINTFS if ( isprint((unsigned char) readChar) ) printf( "%c ", readChar ); else printf( "%x ", readChar ); #endif if ( (unsigned long)(tstr - buf) < bufLen - 1 ) *tstr++ = readChar; } while ( readChar && readChar != '\n' ); *tstr = '\0'; #ifdef DEBUG_PRINTFS printf( "\n" ); #endif tstr = buf; compareLen = strlen(kPasswordServerErrPrefixStr); if ( strncmp( tstr, kPasswordServerErrPrefixStr, compareLen ) == 0 ) { tstr += compareLen; // check if err is a PasswordServer or SASL error compareLen = strlen(kPasswordServerSASLErrPrefixStr); if ( strncmp(tstr, kPasswordServerSASLErrPrefixStr, compareLen) == 0 ) { tstr += compareLen; result.type = kSASLError; } sscanf( tstr, "%d", &result.err ); } return result; } /* remove \r\n at end of the line */ static void chop(char *s) { char *p; if (s==NULL) return; p = s + strlen(s) - 1; if (p[0] == '\n') { *p-- = '\0'; } if (p >= s && p[0] == '\r') { *p-- = '\0'; } } // -------------------------------------------------------------------------------- // ConvertBinaryToHex // -------------------------------------------------------------------------------- void ConvertBinaryToHex( const unsigned char *inData, unsigned long inLen, char *outHexStr ) { const unsigned char *sptr = inData; char *tptr = outHexStr; unsigned long index; char high, low; for ( index = 0; index < inLen; index++ ) { high = (*sptr & 0xF0) >> 4; low = (*sptr & 0x0F); if ( high >= 0x0A ) *tptr++ = (high - 0x0A) + 'A'; else *tptr++ = high + '0'; if ( low >= 0x0A ) *tptr++ = (low - 0x0A) + 'A'; else *tptr++ = low + '0'; sptr++; } *tptr = '\0'; } // -------------------------------------------------------------------------------- // ConvertHexToBinary // -------------------------------------------------------------------------------- void ConvertHexToBinary( const char *inHexStr, unsigned char *outData, unsigned long *outLen ) { unsigned char *tptr = outData; unsigned char val; while ( *inHexStr && *(inHexStr+1) ) { if ( *inHexStr >= 'A' ) val = (*inHexStr - 'A' + 0x0A) << 4; else val = (*inHexStr - '0') << 4; inHexStr++; if ( *inHexStr >= 'A' ) val += (*inHexStr - 'A' + 0x0A); else val += (*inHexStr - '0'); inHexStr++; *tptr++ = val; } *outLen = (tptr - outData); } // -------------------------------------------------------------------------------- // ConvertBinaryTo64 // // Since Base64 rounds up to the nearest multiple of 4/3, prepend // the original length of the data to the string. // -------------------------------------------------------------------------------- int ConvertBinaryTo64( const char *inData, unsigned long inLen, char *outHexStr ) { int result; unsigned int outLen; char *tempBuf; unsigned long bufLen = (inLen+3) * 4 / 3 + 1; tempBuf = (char *) malloc( bufLen + 1 ); if ( tempBuf == NULL ) return kAuthOtherError; result = sasl_encode64( (char *)inData, inLen, tempBuf, bufLen, &outLen ); tempBuf[outLen] = '\0'; sprintf( outHexStr, "{%lu}%s", inLen, tempBuf ); free( tempBuf ); return result; } // -------------------------------------------------------------------------------- // Convert64ToBinary // -------------------------------------------------------------------------------- int Convert64ToBinary( const char *inHexStr, char *outData, unsigned long *outLen ) { int result; unsigned int sasl_outlen; unsigned long attached_outlen = 0; const char *readPtr = inHexStr; // get the original length if ( *readPtr == '{' ) { sscanf( readPtr + 1, "%lu", &attached_outlen ); readPtr = strchr( readPtr, '}' ); if ( readPtr == NULL ) return kAuthOtherError; readPtr++; } result = sasl_decode64( readPtr, strlen(readPtr), (char *)outData, 256, &sasl_outlen ); *outLen = (attached_outlen > 0) ? attached_outlen : (unsigned long)sasl_outlen; return result; } //------------- int getrealm(void *context /*__attribute__((unused))*/, int id, const char **availrealms, const char **result) { /* paranoia check */ if (id != SASL_CB_GETREALM) return SASL_BADPARAM; if (!result) return SASL_BADPARAM; if (availrealms != NULL) *result = *availrealms; return SASL_OK; } int simple(void *context /*__attribute__((unused))*/, int id, const char **result, unsigned *len) { #ifdef DEBUG_PRINTFS printf("in simple\n"); #endif /* paranoia check */ if (! result) return SASL_BADPARAM; switch (id) { case SASL_CB_USER: case SASL_CB_AUTHNAME: //printf("please enter an authorization id: "); *result = ((sPSContextData *)context)->username; if (len != NULL) *len = strlen(((sPSContextData *)context)->username); #ifdef DEBUG_PRINTFS printf("simple - user = %s (len = %d)\n", *result, *len); #endif break; default: return SASL_BADPARAM; } return SASL_OK; } int getsecret(sasl_conn_t *conn, void *context /*__attribute__((unused))*/, int id, sasl_secret_t **psecret) { /* char *password = NULL; */ size_t len = 0; sasl_secret_t *xsec = NULL; #ifdef DEBUG_PRINTFS printf("in getsecret\n"); #endif /* paranoia check */ if (! conn || ! psecret || id != SASL_CB_PASS) return SASL_BADPARAM; *psecret = NULL; /* password = getpassphrase("Password: "); if (! password) return SASL_FAIL; */ if (((sPSContextData *)context)->password) { len = strlen(((sPSContextData *)context)->password); xsec = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len + 1); if ( xsec == NULL ) return SASL_NOMEM; xsec->len = len; strcpy((char *)xsec->data, ((sPSContextData *)context)->password); } *psecret = xsec; return SASL_OK; } //--------- int getconn(const char *host, const char *port) { char servername[1024]; char *endPtr = NULL; struct sockaddr_in sin; int rc; struct in_addr inetAddr; struct hostent *hp; int sock; strncpy(servername, host, sizeof(servername) - 1); servername[sizeof(servername) - 1] = '\0'; /* map hostname -> IP */ rc = inet_aton(servername, &inetAddr); if ( rc == 1 ) { sin.sin_addr.s_addr = inetAddr.s_addr; } else { if ((hp = gethostbyname(servername)) == NULL) { #ifdef DEBUG_PRINTFS perror("gethostbyname"); #endif return -1; } memcpy(&sin.sin_addr, hp->h_addr, hp->h_length); } /* map port -> num */ sin.sin_port = htons(strtol(port, &endPtr, 10)); if ((sin.sin_port == 0) || (endPtr == port)) { #ifdef DEBUG_PRINTFS printf("port '%s' unknown\n", port); #endif return -1; } sin.sin_family = AF_INET; /* connect */ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { #ifdef DEBUG_PRINTFS perror("socket"); #endif return -1; } if (connect(sock, (struct sockaddr *) &sin, sizeof (sin)) < 0) { #ifdef DEBUG_PRINTFS perror("connect"); #endif return -1; } return sock; } // --------------------------------------------------------------------------- // * GetRSAPublicKey // --------------------------------------------------------------------------- int GetRSAPublicKey( sPSContextData *inContext ) { PWServerError serverResult; char buf[1024]; char *keyStr; int bits = 0; if ( inContext == NULL ) return kAuthOtherError; // get string writeToServer( inContext->serverOut, "RSAPUBLIC\r\n" ); serverResult = readFromServer( inContext->serverIn, buf, 1024 ); if (serverResult.err != 0) { #ifdef DEBUG_PRINTFS printf("no public key\n"); #endif return kAuthKeyError; } chop(buf); inContext->rsaPublicKeyStr = (char *) calloc(1, strlen(buf)+1); if ( inContext->rsaPublicKeyStr == NULL ) return kAuthKeyError; strcpy(inContext->rsaPublicKeyStr, buf + 4); // get as struct inContext->rsaPublicKey = key_new(KEY_RSA); if ( inContext->rsaPublicKey == NULL ) return kAuthKeyError; keyStr = buf + 4; bits = key_read(inContext->rsaPublicKey, &keyStr); if (bits == 0) { #ifdef DEBUG_PRINTFS printf("no bits\n"); #endif return kAuthKeyError; } return kAuthNoError; } int InitConnection(sPSContextData* pContext, char* psName, char* rsakey) { int result; unsigned count; char *tptr, *end; sasl_security_properties_t secprops = {0,65535,4096,0,NULL,NULL}; int salen; pContext->fd = -1; // 2.x char localaddr[NI_MAXHOST + NI_MAXSERV], remoteaddr[NI_MAXHOST + NI_MAXSERV]; char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; struct sockaddr_storage local_ip, remote_ip; char buf[kBigBuffSize]; static sasl_callback_t callbacks[5]; // callbacks we support callbacks[0].id = SASL_CB_GETREALM; callbacks[0].proc = (sasl_cbproc *)&getrealm; callbacks[0].context = pContext; callbacks[1].id = SASL_CB_USER; callbacks[1].proc = (sasl_cbproc *)&simple; callbacks[1].context = pContext; callbacks[2].id = SASL_CB_AUTHNAME; callbacks[2].proc = (sasl_cbproc *)&simple; callbacks[2].context = pContext; callbacks[3].id = SASL_CB_PASS; callbacks[3].proc = (sasl_cbproc *)&getsecret; callbacks[3].context = pContext; callbacks[4].id = SASL_CB_LIST_END; callbacks[4].proc = NULL; callbacks[4].context = NULL; result = sasl_client_init(callbacks); if ( result != SASL_OK ) { #ifdef DEBUG_PRINTFS printf("sasl_client_init failed.\n"); #endif return kAuthOtherError; } // connect to remote server pContext->fd = getconn(psName, "106"); if (pContext->fd == -1) return kAuthOtherError; // set ip addresses salen = sizeof(local_ip); if (getsockname(pContext->fd, (struct sockaddr *)&local_ip, &salen) < 0) { perror("getsockname"); } getnameinfo((struct sockaddr *)&local_ip, salen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV); snprintf(localaddr, sizeof(localaddr), "%s;%s", hbuf, pbuf); salen = sizeof(remote_ip); if (getpeername(pContext->fd, (struct sockaddr *)&remote_ip, &salen) < 0) { #ifdef DEBUG_PRINTFS perror("getpeername"); #endif } getnameinfo((struct sockaddr *)&remote_ip, salen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV); snprintf(remoteaddr, sizeof(remoteaddr), "%s;%s", hbuf, pbuf); result = sasl_client_new("rcmd", psName, localaddr, remoteaddr, NULL, 0, &pContext->conn); if ( result != SASL_OK || pContext->conn == NULL ) { #ifdef DEBUG_PRINTFS printf("sasl_client_new failed.\n"); #endif return kAuthOtherError; } result = sasl_setprop(pContext->conn, SASL_SEC_PROPS, &secprops); pContext->serverIn = fdopen(pContext->fd, "r"); pContext->serverOut = fdopen(pContext->fd, "w"); // yank hi there text readFromServer(pContext->serverIn, buf, sizeof(buf)); // retrieve the password server's public RSA key result = GetRSAPublicKey(pContext); if ( result != kAuthNoError ) { return result; } writeToServer(pContext->serverOut, "LIST\r\n"); readFromServer(pContext->serverIn, buf, sizeof(buf)); chop(buf); tptr = buf; for (count=0; tptr; count++ ) { tptr = strchr( tptr, ' ' ); if (tptr) tptr++; } if (count > 0) { pContext->mech = (AuthMethName *)calloc(count, sizeof(AuthMethName)); if ( pContext->mech == NULL ) return( kAuthOtherError ); pContext->mechCount = count; } tptr = strstr(buf, kSASLListPrefix); if ( tptr ) { tptr += strlen(kSASLListPrefix); for ( ; tptr && count > 0; count-- ) { if ( *tptr == '\"' ) tptr++; else break; end = strchr(tptr, '\"'); if (end) *end = '\0'; strcpy(pContext->mech[count-1].method, tptr); #ifdef DEBUG_PRINTFS printf("mech=%s\n", tptr); #endif tptr = end; if ( tptr ) tptr+=2; } } return kAuthNoError; } int DoSASLAuth( sPSContextData *inContext, const char *userName, const char *password, const char *inMechName ) { int siResult = kAuthOtherError; int nameLen = 0; int pwdLen = 0; // need username length, password length, and username must be at least 1 character // Get the length of the user name nameLen = strlen(userName); // Get the length of the user password pwdLen = strlen(password); //TODO yes do it here { char buf[kBigBuffSize]; const char *data; char dataBuf[kBigBuffSize]; const char *chosenmech = NULL; unsigned int len = 0; int r; PWServerError serverResult; // attach the username and password to the sasl connection's context // set these before calling sasl_client_start strncpy(inContext->username, userName, 35); inContext->username[34] = '\0'; inContext->password = password; #ifdef DEBUG_PRINTFS const char **gmechs = sasl_global_listmech(); for (r=0; gmechs[r] != NULL; r++) fprintf(stderr, "gmech=%s\n", gmechs[r]); #endif r = sasl_client_start(inContext->conn, inMechName, NULL, &data, &len, &chosenmech); #ifdef DEBUG_PRINTFS printf("chosenmech=%s, datalen=%u\n", chosenmech, len); #endif if (r != SASL_OK && r != SASL_CONTINUE) { #ifdef DEBUG_PRINTFS printf("starting SASL negotiation, err=%d\n", r); #endif return kAuthSASLError; } // set a user #ifdef DEBUG_PRINTFS printf("USER %s\r\n", userName); #endif snprintf(dataBuf, sizeof(dataBuf), "USER %s\r\n", userName); writeToServer(inContext->serverOut, dataBuf); // flush the read buffer; don't care what the response is for now readFromServer( inContext->serverIn, buf, sizeof(buf) ); // send the auth method dataBuf[0] = 0; if ( len > 0 ) ConvertBinaryToHex( (const unsigned char *)data, len, dataBuf ); #ifdef DEBUG_PRINTFS printf("AUTH %s %s\r\n", chosenmech, dataBuf); #endif if ( len > 0 ) snprintf(buf, sizeof(buf), "AUTH %s %s\r\n", chosenmech, dataBuf); else snprintf(buf, sizeof(buf), "AUTH %s\r\n", chosenmech); writeToServer(inContext->serverOut, buf); // get server response serverResult = readFromServer(inContext->serverIn, buf, sizeof(buf)); if (serverResult.err != 0) { #ifdef DEBUG_PRINTFS printf("server returned an error, err=%d\n", serverResult); #endif return kAuthenticationError; } chop(buf); len = strlen(buf); while ( 1 ) { // skip the "+OK " at the begining of the response if ( len >= 3 && strncmp( buf, "+OK", 3 ) == 0 ) { if ( len > 4 ) { unsigned long binLen; ConvertHexToBinary( buf + 4, (unsigned char *) dataBuf, &binLen ); r = sasl_client_step(inContext->conn, dataBuf, binLen, NULL, &data, &len); } else { // we're done data = NULL; len = 0; r = SASL_OK; } } else r = -1; if (r != SASL_OK && r != SASL_CONTINUE) { #ifdef DEBUG_PRINTFS printf("sasl_client_step=%d\n", r); #endif return kAuthSASLError; } if (data && len != 0) { #ifdef DEBUG_PRINTFS printf("sending response length %d...\n", len); #endif ConvertBinaryToHex( (const unsigned char *)data, len, dataBuf ); #ifdef DEBUG_PRINTFS printf("AUTH2 %s\r\n", dataBuf); #endif fprintf(inContext->serverOut, "AUTH2 %s\r\n", dataBuf ); fflush(inContext->serverOut); } else if (r==SASL_CONTINUE) { #ifdef DEBUG_PRINTFS printf("sending null response...\n"); #endif //send_string(out, "", 0); fprintf(inContext->serverOut, "AUTH2 \r\n" ); fflush(inContext->serverOut); } else break; serverResult = readFromServer(inContext->serverIn, buf, sizeof(buf) ); if ( serverResult.err != 0 ) { #ifdef DEBUG_PRINTFS printf("server returned an error, err=%d\n", serverResult); #endif return kAuthenticationError; } chop(buf); len = strlen(buf); if ( r != SASL_CONTINUE ) break; } return r; } return( siResult ); } // DoSASLAuth int DoRSAValidation ( sPSContextData *inContext, const char *inUserKey ) { int siResult = kAuthNoError; PWServerError serverResult; char buf[1024]; BIGNUM *nonce; BN_CTX *ctx; char *bnStr; int len; if ( inContext == NULL ) return kAuthOtherError; // make sure we are talking to the right server if ( strcmp(inContext->rsaPublicKeyStr, inUserKey) != 0 ) return kAuthKeyError; // make nonce nonce = BN_new(); if ( nonce == NULL ) return kAuthOtherError; // Generate a random challenge BN_rand(nonce, 256, 0, 0); ctx = BN_CTX_new(); BN_mod(nonce, nonce, inContext->rsaPublicKey->rsa->n, ctx); BN_CTX_free(ctx); bnStr = BN_bn2dec(nonce); #ifdef DEBUG_PRINTFS printf("nonce = %s\n", bnStr); #endif int nonceLen = strlen(bnStr); char *encodedStr = (char *)malloc(1024); if ( encodedStr == NULL ) return kAuthOtherError; len = RSA_public_encrypt(nonceLen, (unsigned char *)bnStr, (unsigned char *)encodedStr, inContext->rsaPublicKey->rsa, RSA_PKCS1_PADDING); if (len <= 0) { #ifdef DEBUG_PRINTFS printf("rsa_public_encrypt() failed"); #endif return kAuthKeyError; } if ( ConvertBinaryTo64( encodedStr, (unsigned)len, buf ) == SASL_OK ) { char writeBuf[1024]; unsigned long encodedStrLen; snprintf( writeBuf, sizeof(writeBuf), "RSAVALIDATE %s\r\n", buf ); writeToServer( inContext->serverOut, writeBuf ); serverResult = readFromServer( inContext->serverIn, buf, sizeof(buf) ); if ( Convert64ToBinary( buf + 4, encodedStr, &encodedStrLen ) == SASL_OK ) { encodedStr[nonceLen] = '\0'; #ifdef DEBUG_PRINTFS printf("nonce = %s\n", encodedStr); #endif if (memcmp(bnStr, encodedStr, nonceLen) != 0) siResult = kAuthKeyError; } } return siResult; } int CloseConnection ( sPSContextData *pContext ) { int siResult = kAuthNoError; char buf[1024]; //TODO do whatever to close out the context writeToServer(pContext->serverOut, "QUIT\r\n"); // crashes readFromServer( pContext->serverIn, buf, sizeof(buf) ); return( siResult ); } // CloseConnection // --------------------------------------------------------------------------- // * CleanContextData // --------------------------------------------------------------------------- int CleanContextData ( sPSContextData *inContext ) { int siResult = kAuthNoError; if ( inContext == NULL ) { siResult = kAuthOtherError; } else { // if (inContext->psName != NULL) // { // free( inContext->psName ); // inContext->psName = NULL; // } inContext->offset = 0; if (inContext->conn != NULL) { sasl_dispose(&inContext->conn); inContext->conn = NULL; } if (inContext->serverIn != NULL) { fclose(inContext->serverIn); inContext->serverIn = NULL; } if (inContext->serverOut != NULL) { fclose(inContext->serverOut); inContext->serverOut = NULL; } if (inContext->fd != -1) { close(inContext->fd); inContext->fd = -1; } if (inContext->rsaPublicKeyStr != NULL) { free(inContext->rsaPublicKeyStr); inContext->rsaPublicKeyStr = NULL; } if (inContext->rsaPublicKey != NULL) { key_free(inContext->rsaPublicKey); inContext->rsaPublicKey = NULL; } if (inContext->mech != NULL) { free(inContext->mech); inContext->mech = NULL; } inContext->mechCount = 0; memset(inContext->username, 0, sizeof(inContext->username)); if (inContext->password != NULL) inContext->password = NULL; } return( siResult ); } // CleanContextData int ParseAuthorityData(char* inAuthAuthorityData, char** vers, char** type, char** id, char** addr, char** key) { char* temp; *vers = inAuthAuthorityData; temp = strchr(*vers, ';'); if (temp == NULL) return 0; *temp = '\0'; *type = temp+1; temp = strchr(*type, ';'); if (temp == NULL) return 0; *temp = '\0'; *id = temp+1; temp = strchr(*id, ','); if (temp == NULL) return 0; *temp = '\0'; *key = temp+1; temp = strchr(*key, ':'); if (temp == NULL) return 0; *temp = '\0'; *addr = temp+1; if ( strncmp( *addr, "ipv4/", 5 ) == 0 ) *addr += 5; else if ( strncmp( *addr, "ipv6/", 5 ) == 0 ) *addr += 5; else if ( strncmp( *addr, "dns/", 4 ) == 0 ) *addr += 4; return 1; } int CheckAuthType(char* inAuthAuthorityData, char* authType) { char* temp; temp = strchr(inAuthAuthorityData, ';'); return ((temp != NULL) && (strncmp(temp+1, authType, strlen(authType)) == 0)); } int DoPSAuth(char* userName, char* password, char* inAuthAuthorityData) { sPSContextData context; char* infoVersion = NULL; char* authType = NULL; char* userID = NULL; char* serverAddr = NULL; char* rsaKey = NULL; char* authDataCopy; // copy to our own buffer authDataCopy = malloc(strlen(inAuthAuthorityData) + 1); if ( authDataCopy == NULL ) return kAuthOtherError; strcpy(authDataCopy, inAuthAuthorityData); if (!ParseAuthorityData(authDataCopy, &infoVersion, &authType, &userID, &serverAddr, &rsaKey)) return kAuthOtherError; // check version??? // if (strcmp(infoVersion, "1")) return kAuthOtherError; // check auth info type if (strcmp(authType, PASSWORD_SERVER_AUTH_TYPE) != 0) return kAuthOtherError; int result = InitConnection(&context, serverAddr, rsaKey); if (result != kAuthNoError) return result; if (rsaKey != NULL) { result = DoRSAValidation(&context, rsaKey); } if (result == kAuthNoError) result = DoSASLAuth(&context, userID, password, "DIGEST-MD5"); CloseConnection(&context); CleanContextData(&context); free(authDataCopy); return result; }