Skip to content

Commit

Permalink
New credentials functions drafted
Browse files Browse the repository at this point in the history
  • Loading branch information
cpiker committed Dec 12, 2023
1 parent 51bb8b1 commit 16c6176
Show file tree
Hide file tree
Showing 2 changed files with 342 additions and 45 deletions.
272 changes: 242 additions & 30 deletions das2/credentials.c
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/* Copyright (C) 2017 Chris Piker <[email protected]>
/* Copyright (C) 2017-2023 Chris Piker <[email protected]>
*
* This file is part of libdas2, the Core Das2 C Library.
* This file is part of das2C, the Core Das2 C Library.
*
* Libdas2 is free software; you can redistribute it and/or modify it under
* das2C is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 2.1 as published
* by the Free Software Foundation.
*
* Libdas2 is distributed in the hope that it will be useful, but WITHOUT ANY
* das2C is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 2.1 along with libdas2; if not, see <http://www.gnu.org/licenses/>.
* version 2.1 along with das2C; if not, see <http://www.gnu.org/licenses/>.
*/

#define _POSIX_C_SOURCE 200112L
Expand All @@ -22,6 +22,7 @@

#include "util.h"
#include "array.h"
#include "log.h"

#ifdef _WIN32
#include <windows.h>
Expand Down Expand Up @@ -107,7 +108,7 @@ static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'4', '5', '6', '7', '8', '9', '+', '/'};
static int mod_table[] = {0, 2, 1};

char* base64_encode(
char* das_b64_encode(
const unsigned char *data, size_t input_length, size_t *output_length
) {

Expand Down Expand Up @@ -148,20 +149,20 @@ bool das_cred_init(

if(sServer == NULL) uLen = 0;
else uLen = strlen(sServer);
if((uLen < 4)||(uLen > 127)){sWhich = sServer; }
if((uLen < 4)||(uLen > (DASCRED_SRV_SZ-1))){sWhich = sServer; }

if(sRealm == NULL) uLen = 0;
else uLen = strlen(sRealm);
if((uLen < 4)||(uLen > 127)){sWhich = sRealm; }
if((uLen < 4)||(uLen > (DASCRED_REALM_SZ-1))){sWhich = sRealm; }

if(sHash == NULL) uLen = 0;
else uLen = strlen(sHash);
if((uLen < 2)||(uLen > 255)){sWhich = sHash; }
if((uLen < 2)||(uLen > (DASCRED_HASH_SZ-1))){sWhich = sHash; }

/* Dataset string can be null */
if(sDataset != NULL){
uLen = strlen(sDataset);
if((uLen < 2)||(uLen > 255)){sWhich = sDataset; }
if((uLen < 2)||(uLen > (DASCRED_DSET_SZ-1))){sWhich = sDataset; }
}

if(sWhich){
Expand All @@ -170,10 +171,10 @@ bool das_cred_init(
}

memset(pCred, 0, sizeof(das_credential));
strncpy(pCred->sServer, sServer, 127);
strncpy(pCred->sRealm, sRealm, 127);
strncpy(pCred->sHash, sHash, 255);
if(sDataset != NULL) strncpy(pCred->sDataset, sDataset, 127);
strncpy(pCred->sServer, sServer, DASCRED_SRV_SZ-1);
strncpy(pCred->sRealm, sRealm, DASCRED_REALM_SZ-1);
strncpy(pCred->sHash, sHash, DASCRED_HASH_SZ-1);
if(sDataset != NULL) strncpy(pCred->sDataset, sDataset, DASCRED_DSET_SZ-1);

pCred->bValid = true; /* Assume it works until proven otherwise */

Expand All @@ -192,20 +193,22 @@ DasCredMngr* new_CredMngr(const char* sKeyStore)

DasCredMngr* pThis = (DasCredMngr*)calloc(1, sizeof(DasCredMngr));

das_credential* pFill = (das_credential*)calloc(1, sizeof(das_credential));
das_credential fill;
memset(&fill, 0, sizeof(das_credential));

pThis->pCreds = new_DasAry(
"cashed_credentials",vtUnknown, sizeof(DasCredMngr), (const byte*)pFill,
"cashed_credentials",vtUnknown, sizeof(das_credential), (const byte*)(&fill),
RANK_1(0), UNIT_DIMENSIONLESS
);
pThis->prompt = das_term_prompt;

pThis->sKeyFile = sKeyStore;


if(sKeyStore)
strncpy(pThis->sKeyFile, sKeyStore, 127);

return pThis;
}

void del_DasCredMngr(DasCredMngr* pThis){
void del_CredMngr(DasCredMngr* pThis){
dec_DasAry(pThis->pCreds);
free(pThis);
}
Expand Down Expand Up @@ -250,11 +253,43 @@ int CredMngr_addCred(DasCredMngr* pThis, const das_credential* pCred)
if(pOld == NULL)
DasAry_append(pThis->pCreds, (const byte*)pCred, 1);
else
memcpy(pOld->sHash, pCred->sHash, 256); /* Get terminating null */
memcpy(pOld->sHash, pCred->sHash, DASCRED_HASH_SZ); /* Get terminating null */

return DasAry_size(pThis->pCreds);
}

int CredMngr_addUserPass(
DasCredMngr* pThis, const char* sServer, const char* sRealm,
const char* sDataset, const char* sUser, const char* sPass
){
das_credential cred;
memset(&cred, 0, sizeof(das_credential));

char sBuf[DASCRED_HASH_SZ+2]; /* 2 bytes longer than das_credential.sHash
to detect long hash values */
if(strchr(sUser, ':') != NULL){
das_error(DASERR_CRED, "The user name cannot contain a colon, ':', character");
return -1; /* If the error handler allows for a return */
}

/* Hash it */
snprintf(sBuf, DASCRED_HASH_SZ+1, "%s:%s", sUser, sPassword); /* 257 is not an error */
size_t uLen;
char* sHash = das_b64_encode((unsigned char*)sBuf, strlen(sBuf), &uLen);
/*fprintf(stderr, "DEBUG: Print hash: %s, length %zu\n", sHash, uLen); */
if(uLen > (DASCRED_HASH_SZ-1)){
free(sHash);
pThis->sLastAuthMsg[0] = '\0';
das_error(DASERR_CRED, "Username and password are too large for the hash buffer");
return -1;
}

if(! das_cred_init(sServer, sRealm, sDataset, sHash))
return -1; /* Function sets it's own error message */

return CredMngr_addCred(pThis, &cred);
}


const char* CredMngr_getHttpAuth(
DasCredMngr* pThis, const char* sServer, const char* sRealm, const char* sDataset
Expand All @@ -269,8 +304,8 @@ const char* CredMngr_getHttpAuth(
const char* sMsg = NULL;
if(pThis->sLastAuthMsg[0] != '\0') sMsg = pThis->sLastAuthMsg;

char sBuf[258]; /* 258 is not an error, it's 2 bytes longer than
* das_credential.sHash to detect long hash values */
char sBuf[DASCRED_HASH_SZ+2]; /* 2 bytes longer than das_credential.sHash
to detect long hash values */
while(true){

/* So I either don't have a credential, or it's not valid. Get a new
Expand All @@ -284,11 +319,11 @@ const char* CredMngr_getHttpAuth(
}

/* Hash it */
snprintf(sBuf, 257, "%s:%s", sUser, sPassword); /* 257 is not an error */
snprintf(sBuf, DASCRED_HASH_SZ+1, "%s:%s", sUser, sPassword); /* 257 is not an error */
size_t uLen;
char* sHash = base64_encode((unsigned char*)sBuf, strlen(sBuf), &uLen);
char* sHash = das_b64_encode((unsigned char*)sBuf, strlen(sBuf), &uLen);
/*fprintf(stderr, "DEBUG: Print hash: %s, length %zu\n", sHash, uLen); */
if(uLen > 255){
if(uLen > (DASCRED_HASH_SZ-1)){
free(sHash);
pThis->sLastAuthMsg[0] = '\0';
das_error(DASERR_CRED, "Base64 output buffer is too small, tell "
Expand Down Expand Up @@ -328,7 +363,7 @@ void CredMngr_authFailed(
if(pCred != NULL) pCred->bValid = false;

if(sMsg != NULL)
strncpy(pThis->sLastAuthMsg, sMsg, 1023);
strncpy(pThis->sLastAuthMsg, sMsg, DASCMGR_MSG_SZ-1);

}

Expand All @@ -338,8 +373,185 @@ das_prompt CredMngr_setPrompt(DasCredMngr* pThis, das_prompt new_prompt){
return old;
}

bool CredMngr_save(const DasCredMngr* pThis, const char* sFile){
/* TODO */
/* TODO: Add openssh password symetric key protection to the credentials file */
int CredMngr_save(DasCredMngr* pThis, const char* sSymKey, const char* sFile)
{

if(sSymKey != NULL){
daslog_error("Symetric key encryption of the credentials file is not yet implemented.");
return -1;
}

/* Write all the current credentials to the given filename */
const char* sOut = (sFile == NULL) ? pThis->sKeyFile : sFile;

if(sOut[0] == '\0'){
daslog_error("Can't save. No credentials file specified either here or"
" in the constructor");
return -1;
}

FILE* pOut = fopen(sOut, "wb");

int nRet = 0;
das_credential* pCred = NULL;
for(ptrdiff_t i = 0; i < DasAry_size(pThis->pCreds); ++i){
pCred = (das_credential*)DasAry_getAt(pThis->pCreds, vtUnknown, IDX0(i));
if(!pCred->bValid)
continue;

if(pCred->sDataset[0] != '\0')
fprintf(pOut,
"%s|%s|dataset|%s|%s\n", pCred->sServer, pCred->sRealm, pCred->sDataset,
pCred->sHash
);
else
fprintf(pOut,
"%s|%s|||%s\n", pCred->sServer, pCred->sRealm, pCred->sHash
);
++nRet;
}

fclose(pOut);

if((sFile != NULL)&&(sFile[0] != '\0')){
memset(pThis->sKeyFile, 0, DASCMGR_FILE_SZ);
strncpy(pThis->sKeyFile, sFile, DASCMGR_FILE_SZ-1);
}

return nRet;
}

/* TODO: Add openssh password symetric key protection to the credentials file */
int CredMngr_load(DasCredMngr* pThis, const char* sSymKey, const char* sFile)
{
if(sSymKey != NULL){
daslog_error("Symetric key encryption of the credentials file is not yet implemented.");
return -1;
}

const char* sIn = (sFile == NULL) ? pThis->sKeyFile : sFile;

if(sIn[0] == '\0'){
daslog_error("Can't load. No credentials file specified either here or"
" in the constructor");
return -1;
}

FILE* pIn = fopen(sIn, "rb");

// Make an array to the side to hold loaded credentials
das_credential fill;
memset(&fill, 0, sizeof(das_credential));

DasAry* pTmpCreds = new_DasAry(
"temp_credentials",vtUnknown, sizeof(das_credential), (const byte*)(&fill),
RANK_1(0), UNIT_DIMENSIONLESS
);

int nCreds = 0;
const size_t uLineLen = DASCRED_SRV_SZ+DASCRED_REALM_SZ+DASCRED_DSET_SZ+DASCRED_HASH_SZ+40;
char aLine[
DASCRED_SRV_SZ+DASCRED_REALM_SZ+DASCRED_DSET_SZ+DASCRED_HASH_SZ+40
] = {'\0'};

return false;
char* aBeg[5] = {NULL};
char* aEnd[5] = {NULL}; // These point at terminating nulls
char* pChar = NULL;

das_credential cred;
int nLine = 0;

size_t iSection;
while(fgets(aLine, uLineLen, pIn)){
++nLine;

memset(aBeg, 0, 5*sizeof(void*));
memset(aEnd, 0, 5*sizeof(void*));

// Section begin and end are the same for empty sections
aBeg[0] = aLine;
aEnd[4] = aLine + strlen(aLine) + 1;
iSection = 0;
for(pChar = aLine; *pChar != '\0'; ++pChar){
if(*pChar == '|'){
aEnd[iSection] = pChar;
*pChar = '\0';
++iSection;
if(iSection > 4) break;

aBeg[iSection] = pChar+1;
}
}

// Only lines with 4 pipes are valid credentials, anything else is text
if(iSection != 4) continue;

// Strip ends
for(iSection = 0; iSection < 5; ++iSection){
if(aBeg[iSection] == aEnd[iSection]) continue;

pChar = aBeg[iSection];
while((pChar < aEnd[iSection]) && ((*pChar == ' ')||(*pChar == '\t'))){
++pChar;
aBeg[iSection] += 1;
}

pChar = aEnd[iSection];
while((pChar >= aBeg[iSection]) && ((*pChar == ' ')||(*pChar == '\t'))){
--pChar;
*(aEnd[iSection]) = '\0';
aEnd[iSection] -= 1;
}
}

// Required sections: 0 = server, 1 = Realm, 4 = Hash
if((aBeg[0] == aEnd[0])||(aBeg[1] == aEnd[1])||(aBeg[4] == aEnd[4]))
continue;

// Expect the key 'dataset' if aEnd[2] is not null
if((*(aEnd[2]) != '\0')&&(strcmp(aBeg[2], "dataset") != 0)){
daslog_warn_v(
"%s,%d: Hashes for specific datasets must indicate the key 'dataset'",
sIn, nLine
);
continue;
}

if(das_cred_init(
&cred, aBeg[0], aBeg[1], *(aBeg[3]) == '\0' ? NULL : aBeg[3], aBeg[4]
)){
daslog_warn_v("%s,%d: Could not parse credential", sIn, nLine);
continue;
}

// Add the credential
DasAry_append(pTmpCreds, (const byte*)(&cred), 1);
++nCreds;
}


// Merge in the new credentials from the file
if(nCreds > 0){
das_credential* pNew = NULL;
das_credential* pOld = NULL;
for(ptrdiff_t i = 0; i < DasAry_size(pTmpCreds); ++i){
pNew = (das_credential*)DasAry_getAt(pThis->pCreds, vtUnknown, IDX0(i));

pOld = _CredMngr_getCred(pThis, pNew->sServer, pNew->sRealm, pNew->sDataset, false);
if(pOld == NULL){
DasAry_append(pThis->pCreds, (const byte*)pNew, 1); // append always copies
}
else{
if(pOld->sHash && (strcmp(pOld->sHash, pNew->sHash) != 0))
memcpy(pOld->sHash, pNew->sHash, DASCRED_HASH_SZ);
else
--nCreds;
}
}
}

dec_DasAry(pTmpCreds); // Frees the temporary credentials array

return nCreds;
}
Loading

0 comments on commit 16c6176

Please sign in to comment.