-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
342 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
@@ -22,6 +22,7 @@ | |
|
||
#include "util.h" | ||
#include "array.h" | ||
#include "log.h" | ||
|
||
#ifdef _WIN32 | ||
#include <windows.h> | ||
|
@@ -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 | ||
) { | ||
|
||
|
@@ -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){ | ||
|
@@ -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 */ | ||
|
||
|
@@ -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); | ||
} | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 " | ||
|
@@ -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); | ||
|
||
} | ||
|
||
|
@@ -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; | ||
} |
Oops, something went wrong.