481 lines
19 KiB
C
481 lines
19 KiB
C
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE 1
|
|
#endif
|
|
|
|
#include "../lib/cJSON.h"
|
|
#include "../lib/qrcodegen.h"
|
|
#include "misc.h"
|
|
#include <unistd.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <curl/curl.h>
|
|
#include <security/pam_appl.h>
|
|
#include <security/pam_modules.h>
|
|
|
|
struct MemoryStruct {
|
|
char *memory;
|
|
size_t size;
|
|
};
|
|
|
|
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
|
size_t realsize = size * nmemb;
|
|
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
|
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
|
if(mem->memory == NULL)
|
|
return 0;
|
|
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
|
mem->size += realsize;
|
|
mem->memory[mem->size] = 0;
|
|
return realsize;
|
|
}
|
|
|
|
char *parseNestedJson(char source[], char name[], char indexName[]) {
|
|
const cJSON *value = NULL;
|
|
const cJSON *nestedValue = NULL;
|
|
cJSON *monitor_json = cJSON_Parse(source);
|
|
value = cJSON_GetObjectItemCaseSensitive(monitor_json, name);
|
|
nestedValue = cJSON_GetObjectItemCaseSensitive(value,indexName);
|
|
if(nestedValue)
|
|
return nestedValue->valuestring;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
int parseNestedJsonInt(char source[], char name[], char indexName[]) {
|
|
const cJSON *value = NULL;
|
|
const cJSON *nestedValue = NULL;
|
|
cJSON *monitor_json = cJSON_Parse(source);
|
|
value = cJSON_GetObjectItemCaseSensitive(monitor_json, name);
|
|
nestedValue = cJSON_GetObjectItemCaseSensitive(value,indexName);
|
|
if(nestedValue)
|
|
return nestedValue->valueint;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
char *parseJson(char source[], char name[] ) {
|
|
const cJSON *value = NULL;
|
|
cJSON *monitor_json = cJSON_Parse(source);
|
|
value = cJSON_GetObjectItemCaseSensitive(monitor_json, name);
|
|
if(value)
|
|
return value->valuestring;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void printQr(const uint8_t qrcode[],pam_handle_t *pamh) {
|
|
char *qrCodeString = "";
|
|
char *white = "\u2588\u2588";
|
|
char *black = " ";
|
|
int size = qrcodegen_getSize(qrcode);
|
|
int border = 1;
|
|
int y;
|
|
//additional line above
|
|
for(y = -border; y < size + border + 2; y++){
|
|
asprintf(&qrCodeString,"%s%s", qrCodeString, white);
|
|
}
|
|
asprintf(&qrCodeString,"%s%s", qrCodeString, "\n");
|
|
for(y = -border; y < size + border; y++) {
|
|
int x;
|
|
asprintf(&qrCodeString,"%s%s",qrCodeString,white);
|
|
for(x = -border; x < size + border; x++)
|
|
asprintf(&qrCodeString,"%s%s",qrCodeString,qrcodegen_getModule(qrcode, x, y) ? black : white);
|
|
asprintf(&qrCodeString,"%s%s",qrCodeString,white);
|
|
asprintf(&qrCodeString,"%s%s",qrCodeString,"\n");
|
|
}
|
|
//additional line below
|
|
for(y = -border; y < size + border + 2; y++){
|
|
asprintf(&qrCodeString,"%s%s", qrCodeString, white);
|
|
}
|
|
asprintf(&qrCodeString,"%s%s",qrCodeString,"\n");
|
|
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%s",qrCodeString);
|
|
}
|
|
|
|
void displayQrCode(pam_handle_t *pamh, char *qrToken) {
|
|
enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW;
|
|
uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX];
|
|
uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX];
|
|
bool ok = qrcodegen_encodeText(qrToken, tempBuffer, qrcode, errCorLvl, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
|
|
if(ok)
|
|
printQr(qrcode, pamh);
|
|
}
|
|
|
|
void displayAvailableAuthenticationMethods(cJSON *methods, pam_handle_t *pamh) {
|
|
if(cJSON_GetArraySize(methods)) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "");
|
|
int i;
|
|
for (i = 0; i < cJSON_GetArraySize(methods); i++) {
|
|
if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"email") == 0)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: Email Link",i+1);
|
|
if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"qrcode") == 0)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: QR Code",i+1);
|
|
if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"totp") == 0)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: Mobile TOTP",i+1);
|
|
if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"push") == 0)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: Mobile Push",i+1);
|
|
if (strcmp(cJSON_GetArrayItem(methods, i)->valuestring,"sms") == 0)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "%d: SMS code",i+1);
|
|
}
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "");
|
|
}
|
|
}
|
|
|
|
void selectMethodModule(pam_handle_t *pamh, cJSON *methods, char **selectedMethod) {
|
|
char *authentication = 0;
|
|
int idx = 0;
|
|
if(cJSON_GetArraySize(methods)) {
|
|
displayAvailableAuthenticationMethods(methods, pamh);
|
|
do{
|
|
if(cJSON_GetArraySize(methods) == 1) {
|
|
idx=1;
|
|
break;
|
|
}
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &authentication, "Select method [1-%d]: ",cJSON_GetArraySize(methods));
|
|
idx = atoi(authentication);
|
|
}while(idx < 1 || idx > cJSON_GetArraySize(methods));
|
|
*selectedMethod=cJSON_GetArrayItem(methods, idx-1)->valuestring;
|
|
}
|
|
}
|
|
|
|
cJSON *returnAvailableMethods(cJSON *methods, pam_handle_t *pamh) {
|
|
int methodsCount = 5, i, jsonIndex;
|
|
char *supportedMethods[] = {"email","qrcode","totp","push","sms"};
|
|
cJSON* result = cJSON_CreateArray();
|
|
|
|
for (i= 0 ; i < methodsCount ; i++){
|
|
cJSON* foundMethod = cJSON_GetObjectItem(methods, supportedMethods[i]);
|
|
for (jsonIndex = 0 ; jsonIndex < cJSON_GetArraySize(methods) ; jsonIndex ++ ){
|
|
if (strcmp(cJSON_GetArrayItem(methods, jsonIndex)->valuestring, supportedMethods[i]) == 0){
|
|
foundMethod = cJSON_GetArrayItem(methods,jsonIndex);
|
|
break;
|
|
}
|
|
}
|
|
if (foundMethod){
|
|
cJSON_AddItemReferenceToArray(result, foundMethod);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void displaySelectedMethodMsg(pam_handle_t *pamh, char *selectedMethod) {
|
|
if (strcmp(selectedMethod, "email") == 0) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nWe have sent a verification link.\nUse this link to sign in to your account.");
|
|
} else if (strcmp(selectedMethod, "qrcode") == 0) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nScan QR code using Rublon Authenticator:");
|
|
} else if (strcmp(selectedMethod, "push") == 0) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nLogin request via Mobile Push sent to phone ...");
|
|
}
|
|
}
|
|
|
|
void displayExceptionStatus(pam_handle_t *pamh, char *exception) {
|
|
if (strcmp(exception, "UserBypassedException") == 0) {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "User bypassed.");
|
|
} else if (strcmp(exception, "TransactionAccessTokenExpiredException") == 0 || strcmp(exception, "TransactionExpiredException") == 0) {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "Session expired. Please try again.");
|
|
} else if (strcmp(exception, "TransactionIdExpiredException") == 0) {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "Session finally expired.");
|
|
} else if (strcmp(exception, "SendMessageException") == 0) {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "Cannot send SMS message. Please check your phone number or try again later.");
|
|
} else {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "%s", exception);
|
|
}
|
|
}
|
|
|
|
bool transactionErrorException(pam_handle_t *pamh, char *curlResponse) {
|
|
char *exception = NULL;
|
|
exception = parseNestedJson(curlResponse,"result","exception");
|
|
if(exception != NULL) {
|
|
displayExceptionStatus(pamh, exception);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
char *curlHandler(pam_handle_t *pamh, char *jsonObj, char *url, char *secretKey) {
|
|
CURL *curl;
|
|
CURLcode res, headerLength;
|
|
struct MemoryStruct chunks;
|
|
chunks.memory = malloc(1);
|
|
chunks.size = 0;
|
|
curl = curl_easy_init();
|
|
char *curlResponse, *responseXRublon;
|
|
char *xRublon;
|
|
|
|
asprintf(&xRublon,"X-Rublon-Signature: %s",signData(pamh,jsonObj, secretKey));
|
|
|
|
if(curl) {
|
|
struct curl_slist *chunk = NULL;
|
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
|
chunk = curl_slist_append(chunk, "Content-Type: application/json");
|
|
chunk = curl_slist_append(chunk, "Accept: application/json");
|
|
chunk = curl_slist_append(chunk, xRublon);
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
|
|
curl_easy_setopt(curl, CURLOPT_HEADER, 1);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunks);
|
|
res = curl_easy_perform(curl);
|
|
if(res != CURLE_OK)
|
|
return NULL;
|
|
long size;
|
|
curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size);
|
|
curlResponse = curlResponseJsonParser(pamh, size, chunks.memory);
|
|
responseXRublon = curlResponseSignatureParser(pamh, size, chunks.memory);
|
|
if(!verifyData(pamh, curlResponse, secretKey, responseXRublon)) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Invalid Signature!");
|
|
return NULL;
|
|
}
|
|
curl_easy_cleanup(curl);
|
|
if(chunks.memory)
|
|
free(chunks.memory);
|
|
return curlResponse;
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
bool checkResult(char *curlResponse) {
|
|
cJSON *json = cJSON_Parse(curlResponse);
|
|
cJSON *result_json = cJSON_GetObjectItemCaseSensitive(json, "result");
|
|
cJSON_Delete(json);
|
|
if (cJSON_IsTrue(result_json) == 1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool isDigit(char *code) {
|
|
int i;
|
|
for (i=0;i<strlen(code); i++)
|
|
if (!isdigit(code[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool isOneOfSelectedMethods(char *selectedMethod, char *methods[], int methodsSize) {
|
|
int i;
|
|
for (i = 0; i < methodsSize; i++)
|
|
if (strcmp(selectedMethod,methods[i]) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
int postInit(pam_handle_t *pamh, cJSON **availableMethods, struct ApplicationInfoStruct* info, char **transactionId, char *systemToken, char *secretKey, const char *appUserId, char *userEmail, char *rublonApiServer) {
|
|
char *status = NULL;
|
|
char *transactionStatus = NULL;
|
|
char *exception = NULL;
|
|
int gdprAccepted = 0;
|
|
int tosAccepted = 0;
|
|
char *policy = NULL;
|
|
char *tid = NULL;
|
|
char *jsonObj;
|
|
char *url;
|
|
|
|
asprintf(&jsonObj,"{\"systemToken\":\"%s\",\"appUserId\":\"%s\",\"userEmail\":\"%s\"}",systemToken,appUserId,userEmail);
|
|
asprintf(&url,"%s/api/transaction/init",rublonApiServer);
|
|
char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey);
|
|
|
|
if(curlResponse == NULL)
|
|
return CONNECTION_ERROR;
|
|
|
|
status = parseJson(curlResponse,"status");
|
|
exception = parseNestedJson(curlResponse,"result","exception");
|
|
transactionStatus = parseNestedJson(curlResponse,"result","status");
|
|
*transactionId = parseNestedJson(curlResponse,"result","tid");
|
|
|
|
cJSON *resp = cJSON_GetObjectItem(cJSON_Parse(curlResponse),"result");
|
|
cJSON *methods = cJSON_GetObjectItem(resp,"methods");
|
|
gdprAccepted = parseNestedJsonInt(curlResponse,"result","gdprAccepted");
|
|
tosAccepted = parseNestedJsonInt(curlResponse,"result","tosAccepted");
|
|
*availableMethods = methods;
|
|
|
|
info->companyName = cJSON_GetObjectItem(resp, "companyName")->valuestring;
|
|
info->applicationName = cJSON_GetObjectItem(resp, "applicationName")->valuestring;
|
|
|
|
if(gdprAccepted != 1 || tosAccepted != 1) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nYou have to read and accept our:\n- Terms of Use: https://core.rublon.net/terms_of_use\n- Privacy Policy: https://core.rublon.net/privacy_policy\n");
|
|
do{
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &policy, "I have read and agree Terms of Use and Privacy Policy (press Y): ");
|
|
}while(strcmp(policy,"Y") != 0 && strcmp(policy,"y") != 0);
|
|
}
|
|
if(transactionStatus != NULL && strcmp(transactionStatus,"waiting") == 0) {
|
|
return STATUS_WAITING;
|
|
}
|
|
if(strcmp(status,"ERROR") == 0 && strcmp(exception,"UserBypassedException") == 0)
|
|
return STATUS_BYPASS;
|
|
if(strcmp(status,"ERROR") == 0) {
|
|
if(transactionErrorException(pamh,curlResponse))
|
|
return STATUS_DENIED;
|
|
}
|
|
if(transactionStatus == NULL || transactionId == NULL) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nRublon Transaction Error!");
|
|
return STATUS_BYPASS;
|
|
}
|
|
if(strcmp(transactionStatus,"denied") == 0)
|
|
return STATUS_DENIED;
|
|
if(strcmp(transactionStatus,"pending") == 0)
|
|
return STATUS_PENDING;
|
|
return STATUS_UNKNOWN;
|
|
}
|
|
|
|
int postMethod(pam_handle_t *pamh, char *secretKey, char *tId, char *selectedMethod, char *rublonApiServer, char *systemToken, bool onlyOneMethod) {
|
|
char *status = NULL;
|
|
char *qrToken = NULL;
|
|
char *transactionId = NULL;
|
|
char *jsonObj;
|
|
char *url;
|
|
char *changeMethod = NULL;
|
|
|
|
asprintf(&jsonObj,"{\"systemToken\":\"%s\",\"tid\":\"%s\",\"method\":\"%s\",\"GDPRAccepted\":\"true\",\"tosAccepted\":\"true\"}",systemToken,tId,selectedMethod);
|
|
asprintf(&url,"%s/api/transaction/methodSSH",rublonApiServer);
|
|
char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey);
|
|
if(curlResponse == NULL)
|
|
return CONNECTION_ERROR;
|
|
status = parseJson(curlResponse,"status");
|
|
qrToken = parseNestedJson(curlResponse,"result","token");
|
|
transactionId = parseNestedJson(curlResponse,"result","tid");
|
|
displaySelectedMethodMsg(pamh, selectedMethod);
|
|
if(qrToken != NULL)
|
|
displayQrCode(pamh, qrToken);
|
|
|
|
char *methodsToCompare[] = {"email","push","qrcode"};
|
|
if(isOneOfSelectedMethods(selectedMethod, methodsToCompare, 3)) {
|
|
if(!onlyOneMethod) {
|
|
do {
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &changeMethod, "Press [Enter] key to continue (or enter 0 to select different method): ");
|
|
if(strcmp(changeMethod,"0") == 0)
|
|
return CHANGE_METHOD;
|
|
}while(strlen(changeMethod) != 0);
|
|
}
|
|
else
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, NULL, "Press enter to continue.");
|
|
}
|
|
|
|
if(strcmp(status,"ERROR") == 0) {
|
|
if(transactionErrorException(pamh,curlResponse))
|
|
return STATUS_DENIED;
|
|
}
|
|
if(transactionId == NULL) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nRublon Transaction Error!");
|
|
return STATUS_BYPASS;
|
|
}
|
|
if(strcmp(status,"OK") == 0) {
|
|
return STATUS_PENDING;
|
|
}
|
|
return STATUS_UNKNOWN;
|
|
}
|
|
|
|
int postConfirmCode(pam_handle_t *pamh, char *secretKey, char *systemToken, char *transactionId, char *selectedMethod, char *rublonApiServer, bool onlyOneMethod) {
|
|
char *status = NULL;
|
|
char *exception = NULL;
|
|
bool result = false;
|
|
char *code;
|
|
char *jsonObj;
|
|
char *url;
|
|
bool firstAttempt = true;
|
|
|
|
do {
|
|
if(!firstAttempt)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "Invalid passcode. Try again!");
|
|
firstAttempt = false;
|
|
if(onlyOneMethod)
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nEnter passcode");
|
|
else
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nEnter passcode (or 0 to select different method):");
|
|
if(strcmp(selectedMethod,"totp") == 0)
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &code, "Mobile TOTP from Rublon Authenticator:");
|
|
else if(strcmp(selectedMethod,"sms") == 0)
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &code, "SMS passcode: ");
|
|
else
|
|
return CONNECTION_ERROR;
|
|
if(strcmp(code,"0") == 0 && !onlyOneMethod)
|
|
return CHANGE_METHOD;
|
|
while(strlen(code) != TOTP_SMS_INPUT_CODE_SIZE || !isDigit(code)){
|
|
pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &code, "Mobile TOTP must contain 6 digits: ");
|
|
if(strcmp(code,"0") == 0)
|
|
return CHANGE_METHOD;
|
|
};
|
|
|
|
asprintf(&jsonObj,"{\"systemToken\":\"%s\",\"tid\":\"%s\",\"vericode\":\"%s\"}",systemToken,transactionId,code);
|
|
asprintf(&url,"%s/api/transaction/confirmCode",rublonApiServer);
|
|
char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey);
|
|
if(curlResponse == NULL)
|
|
return CONNECTION_ERROR;
|
|
result = checkResult(curlResponse);
|
|
status = parseJson(curlResponse,"status");
|
|
exception = parseNestedJson(curlResponse,"result","exception");
|
|
if(strcmp(status,"ERROR") == 0) {
|
|
if(strcmp(exception, "TransactionAccessTokenExpiredException") == 0 || transactionErrorException(pamh,curlResponse))
|
|
return STATUS_DENIED;
|
|
}
|
|
if(status == NULL) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nRublon Transaction Error!");
|
|
return STATUS_BYPASS;
|
|
}
|
|
}while(result == false);
|
|
|
|
if(strcmp(status,"OK") == 0)
|
|
return STATUS_PENDING;
|
|
|
|
return STATUS_UNKNOWN;
|
|
}
|
|
|
|
int postVerifySSH(pam_handle_t *pamh, char *secretKey, char *transactionId, char *selectedMethod, char *rublonApiServer, char *systemToken, char **accessToken) {
|
|
char *jsonObj;
|
|
char *url;
|
|
char *status;
|
|
char *exception;
|
|
|
|
asprintf(&jsonObj,"{\"tid\":\"%s\",\"systemToken\":\"%s\"}",transactionId, systemToken);
|
|
asprintf(&url,"%s/api/transaction/verifySSH",rublonApiServer);
|
|
char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey);
|
|
if(curlResponse == NULL)
|
|
return CONNECTION_ERROR;
|
|
status = parseJson(curlResponse,"status");
|
|
exception = parseNestedJson(curlResponse,"result","exception");
|
|
*accessToken = parseNestedJson(curlResponse,"result","token");
|
|
if(strcmp(status,"ERROR") == 0) {
|
|
if(transactionErrorException(pamh,curlResponse))
|
|
return STATUS_DENIED;
|
|
}
|
|
if(accessToken == NULL) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nRublon Transaction Error!");
|
|
return STATUS_BYPASS;
|
|
}
|
|
if(strcmp(status,"OK") == 0)
|
|
return STATUS_PENDING;
|
|
return STATUS_UNKNOWN;
|
|
}
|
|
|
|
int postCredentials(pam_handle_t *pamh, char *systemToken, char *accessToken, char *rublonApiServer, char *secretKey) {
|
|
char *status = NULL;
|
|
char *answer = NULL;
|
|
char *jsonObj;
|
|
char *url;
|
|
|
|
asprintf(&jsonObj,"{\"systemToken\":\"%s\",\"accessToken\":\"%s\"}",systemToken,accessToken);
|
|
asprintf(&url,"%s/api/transaction/credentials",rublonApiServer);
|
|
|
|
char *curlResponse = curlHandler(pamh, jsonObj, url, secretKey);
|
|
if(curlResponse == NULL)
|
|
return CONNECTION_ERROR;
|
|
|
|
status = parseJson(curlResponse,"status");
|
|
answer = parseNestedJson(curlResponse,"result","answer");
|
|
|
|
if(strcmp(status,"ERROR") == 0)
|
|
return STATUS_DENIED;
|
|
if(answer == NULL) {
|
|
pam_prompt(pamh, PAM_TEXT_INFO, NULL, "\nRublon Transaction Error!");
|
|
return STATUS_BYPASS;
|
|
}
|
|
if( (strcmp(answer,"true") == 0) && (strcmp(status,"OK") == 0))
|
|
return STATUS_CONFIRMED;
|
|
|
|
return STATUS_UNKNOWN;
|
|
}
|