mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-16 21:30:42 +01:00
complete handle multiple blocks in encrypted queue files
This commit is contained in:
parent
3aeafbdfad
commit
afe14ce2f6
@ -42,8 +42,9 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */
|
||||
rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf);
|
||||
rsRetVal (*Decrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf);
|
||||
rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile);
|
||||
void (*SetDeleteOnClose)(void *pFileInstData, int val);
|
||||
rsRetVal (*DeleteStateFiles)(uchar *logfn);
|
||||
rsRetVal (*GetBytesLeftInBlock)(void *pFileInstData, ssize_t *left);
|
||||
void (*SetDeleteOnClose)(void *pFileInstData, int val);
|
||||
ENDinterface(cryprov)
|
||||
#define cryprovCURR_IF_VERSION 3 /* increment whenever you change the interface structure! */
|
||||
#endif /* #ifndef INCLUDED_CRYPROV_H */
|
||||
|
||||
@ -54,6 +54,8 @@
|
||||
|
||||
#define READBUF_SIZE 4096 /* size of the read buffer */
|
||||
|
||||
static rsRetVal rsgcryBlkBegin(gcryfile gf);
|
||||
|
||||
static rsRetVal
|
||||
eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf)
|
||||
{
|
||||
@ -170,11 +172,13 @@ eiGetRecord(gcryfile gf, char *rectype, char *value)
|
||||
int c;
|
||||
DEFiRet;
|
||||
|
||||
c = eiReadChar(gf);
|
||||
if(c == EOF) { ABORT_FINALIZE(RS_RET_NO_DATA); }
|
||||
for(i = 0 ; i < EIF_MAX_RECTYPE_LEN ; ++i) {
|
||||
c = eiReadChar(gf);
|
||||
if(c == ':' || c == EOF)
|
||||
break;
|
||||
rectype[i] = c;
|
||||
c = eiReadChar(gf);
|
||||
}
|
||||
if(c != ':') { ABORT_FINALIZE(RS_RET_ERR); }
|
||||
rectype[i] = '\0';
|
||||
@ -232,6 +236,23 @@ finalize_it:
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
static rsRetVal
|
||||
eiGetEND(gcryfile gf, off64_t *offs)
|
||||
{
|
||||
char rectype[EIF_MAX_RECTYPE_LEN+1];
|
||||
char value[EIF_MAX_VALUE_LEN+1];
|
||||
DEFiRet;
|
||||
|
||||
CHKiRet(eiGetRecord(gf, rectype, value));
|
||||
if(strcmp(rectype, "END")) {
|
||||
DBGPRINTF("no END record found when expected, record type "
|
||||
"seen is '%s'\n", rectype);
|
||||
ABORT_FINALIZE(RS_RET_ERR);
|
||||
}
|
||||
*offs = atoll(value);
|
||||
finalize_it:
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
static rsRetVal
|
||||
eiOpenAppend(gcryfile gf)
|
||||
@ -304,12 +325,33 @@ eiClose(gcryfile gf, off64_t offsLogfile)
|
||||
len = snprintf(offs, sizeof(offs), "%lld", offsLogfile);
|
||||
eiWriteRec(gf, "END:", 4, offs, len);
|
||||
}
|
||||
gcry_cipher_close(gf->chd);
|
||||
free(gf->readBuf);
|
||||
close(gf->fd);
|
||||
gf->fd = -1;
|
||||
DBGPRINTF("encryption info file %s: closed\n", gf->eiName);
|
||||
}
|
||||
|
||||
/* this returns the number of bytes left inside the block or -1, if the block
|
||||
* size is unbounded. The function automatically handles end-of-block and begins
|
||||
* to read the next block in this case.
|
||||
*/
|
||||
rsRetVal
|
||||
gcryfileGetBytesLeftInBlock(gcryfile gf, ssize_t *left)
|
||||
{
|
||||
DEFiRet;
|
||||
if(gf->bytesToBlkEnd == 0) {
|
||||
DBGPRINTF("libgcry: end of current crypto block\n");
|
||||
gcry_cipher_close(gf->chd);
|
||||
CHKiRet(rsgcryBlkBegin(gf));
|
||||
}
|
||||
*left = gf->bytesToBlkEnd;
|
||||
finalize_it:
|
||||
// TODO: remove once this code is sufficiently well-proven
|
||||
DBGPRINTF("gcryfileGetBytesLeftInBlock returns %lld, iRet %d\n", (long long) *left, iRet);
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
/* this is a special functon for use by the rsyslog disk queue subsystem. It
|
||||
* needs to have the capability to delete state when a queue file is rolled
|
||||
* over. This simply generates the file name and deletes it. It must take care
|
||||
@ -363,7 +405,7 @@ gcryfileDestruct(gcryfile gf, off64_t offsLogfile)
|
||||
if(gf == NULL)
|
||||
goto done;
|
||||
|
||||
dbgprintf("DDDD: cryprov closes file %s\n", gf->eiName);
|
||||
DBGPRINTF("libgcry: close file %s\n", gf->eiName);
|
||||
eiClose(gf, offsLogfile);
|
||||
if(gf->bDeleteOnClose) {
|
||||
DBGPRINTF("unlink file '%s' due to bDeleteOnClose set\n", gf->eiName);
|
||||
@ -498,72 +540,111 @@ readIV(gcryfile gf, uchar **iv)
|
||||
rsRetVal localRet;
|
||||
DEFiRet;
|
||||
|
||||
do {
|
||||
localRet = eiOpenRead(gf);
|
||||
if(localRet == RS_RET_EI_NO_EXISTS) {
|
||||
/* wait until it is created */
|
||||
srSleep(0, 10000);
|
||||
} else {
|
||||
CHKiRet(localRet);
|
||||
if(gf->fd == -1) {
|
||||
while(gf->fd == -1) {
|
||||
localRet = eiOpenRead(gf);
|
||||
if(localRet == RS_RET_EI_NO_EXISTS) {
|
||||
/* wait until it is created */
|
||||
srSleep(0, 10000);
|
||||
} else {
|
||||
CHKiRet(localRet);
|
||||
}
|
||||
}
|
||||
} while(localRet != RS_RET_OK);
|
||||
CHKiRet(eiCheckFiletype(gf));
|
||||
CHKiRet(eiCheckFiletype(gf));
|
||||
}
|
||||
*iv = malloc(gf->blkLength); /* do NOT zero-out! */
|
||||
CHKiRet(eiGetIV(gf, *iv, (size_t) gf->blkLength));
|
||||
dbgprintf("DDDD: read %d bytes of IV\n", (int) gf->blkLength);
|
||||
finalize_it:
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
rsRetVal
|
||||
rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode)
|
||||
/* this tries to read the END record. HOWEVER, no such record may be
|
||||
* present, which is the case if we handle a currently-written to queue
|
||||
* file. On the other hand, the queue file may contain multiple blocks. So
|
||||
* what we do is try to see if there is a block end or not - and set the
|
||||
* status accordingly. Note that once we found no end-of-block, we will never
|
||||
* retry. This is because that case can never happen under current queue
|
||||
* implementations. -- gerhards, 2013-05-16
|
||||
*/
|
||||
static inline rsRetVal
|
||||
readBlkEnd(gcryfile gf)
|
||||
{
|
||||
off64_t blkEnd;
|
||||
DEFiRet;
|
||||
|
||||
iRet = eiGetEND(gf, &blkEnd);
|
||||
if(iRet == RS_RET_OK) {
|
||||
gf->bytesToBlkEnd = (ssize_t) blkEnd;
|
||||
} else if(iRet == RS_RET_NO_DATA) {
|
||||
gf->bytesToBlkEnd = -1;
|
||||
} else {
|
||||
FINALIZE;
|
||||
}
|
||||
|
||||
finalize_it:
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
|
||||
/* Read the block begin metadata and set our state variables accordingly. Can also
|
||||
* be used to init the first block in write case.
|
||||
*/
|
||||
static rsRetVal
|
||||
rsgcryBlkBegin(gcryfile gf)
|
||||
{
|
||||
gcry_error_t gcryError;
|
||||
gcryfile gf = NULL;
|
||||
uchar *iv = NULL;
|
||||
DEFiRet;
|
||||
|
||||
CHKiRet(gcryfileConstruct(ctx, &gf, fname));
|
||||
gf->openMode = openMode;
|
||||
|
||||
gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo);
|
||||
|
||||
gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0);
|
||||
gcryError = gcry_cipher_open(&gf->chd, gf->ctx->algo, gf->ctx->mode, 0);
|
||||
if (gcryError) {
|
||||
dbgprintf("gcry_cipher_open failed: %s/%s\n",
|
||||
gcry_strsource(gcryError),
|
||||
gcry_strerror(gcryError));
|
||||
DBGPRINTF("gcry_cipher_open failed: %s/%s\n",
|
||||
gcry_strsource(gcryError), gcry_strerror(gcryError));
|
||||
ABORT_FINALIZE(RS_RET_ERR);
|
||||
}
|
||||
|
||||
gcryError = gcry_cipher_setkey(gf->chd, gf->ctx->key, gf->ctx->keyLen);
|
||||
if (gcryError) {
|
||||
dbgprintf("gcry_cipher_setkey failed: %s/%s\n",
|
||||
gcry_strsource(gcryError),
|
||||
gcry_strerror(gcryError));
|
||||
DBGPRINTF("gcry_cipher_setkey failed: %s/%s\n",
|
||||
gcry_strsource(gcryError), gcry_strerror(gcryError));
|
||||
ABORT_FINALIZE(RS_RET_ERR);
|
||||
}
|
||||
|
||||
if(openMode == 'r') {
|
||||
if(gf->openMode == 'r') {
|
||||
readIV(gf, &iv);
|
||||
readBlkEnd(gf);
|
||||
} else {
|
||||
seedIV(gf, &iv);
|
||||
}
|
||||
|
||||
gcryError = gcry_cipher_setiv(gf->chd, iv, gf->blkLength);
|
||||
if (gcryError) {
|
||||
dbgprintf("gcry_cipher_setiv failed: %s/%s\n",
|
||||
gcry_strsource(gcryError),
|
||||
gcry_strerror(gcryError));
|
||||
DBGPRINTF("gcry_cipher_setiv failed: %s/%s\n",
|
||||
gcry_strsource(gcryError), gcry_strerror(gcryError));
|
||||
ABORT_FINALIZE(RS_RET_ERR);
|
||||
}
|
||||
if(openMode == 'w') {
|
||||
|
||||
if(gf->openMode == 'w') {
|
||||
CHKiRet(eiOpenAppend(gf));
|
||||
CHKiRet(eiWriteIV(gf, iv));
|
||||
}
|
||||
*pgf = gf;
|
||||
finalize_it:
|
||||
free(iv);
|
||||
RETiRet;
|
||||
}
|
||||
|
||||
rsRetVal
|
||||
rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode)
|
||||
{
|
||||
gcryfile gf = NULL;
|
||||
DEFiRet;
|
||||
|
||||
CHKiRet(gcryfileConstruct(ctx, &gf, fname));
|
||||
gf->openMode = openMode;
|
||||
gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo);
|
||||
CHKiRet(rsgcryBlkBegin(gf));
|
||||
*pgf = gf;
|
||||
finalize_it:
|
||||
if(iRet != RS_RET_OK && gf != NULL)
|
||||
gcryfileDestruct(gf, -1);
|
||||
RETiRet;
|
||||
@ -601,6 +682,7 @@ rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len)
|
||||
gcry_error_t gcryError;
|
||||
DEFiRet;
|
||||
|
||||
pF->bytesToBlkEnd -= *len;
|
||||
gcryError = gcry_cipher_decrypt(pF->chd, buf, *len, NULL, 0);
|
||||
if(gcryError) {
|
||||
DBGPRINTF("gcry_cipher_decrypt failed: %s/%s\n",
|
||||
@ -609,7 +691,8 @@ rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len)
|
||||
ABORT_FINALIZE(RS_RET_ERR);
|
||||
}
|
||||
removePadding(buf, len);
|
||||
dbgprintf("DDDD: decrypted, buffer is now '%50.50s'\n", buf);
|
||||
// TODO: remove dbgprintf once things are sufficently stable -- rgerhards, 2013-05-16
|
||||
dbgprintf("libgcry: decrypted, bytesToBlkEnd %lld, buffer is now '%50.50s'\n", (long long) pF->bytesToBlkEnd, buf);
|
||||
|
||||
finalize_it:
|
||||
RETiRet;
|
||||
|
||||
@ -44,6 +44,9 @@ struct gcryfile_s {
|
||||
int16_t readBufIdx;
|
||||
int16_t readBufMaxIdx;
|
||||
int8_t bDeleteOnClose; /* for queue support, similar to stream subsys */
|
||||
ssize_t bytesToBlkEnd; /* number of bytes remaining in current crypto block
|
||||
-1 means -> no end (still being writen to, queue files),
|
||||
0 means -> end of block, new one must be started. */
|
||||
};
|
||||
|
||||
int gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen);
|
||||
@ -60,6 +63,7 @@ rsRetVal rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len);
|
||||
rsRetVal rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len);
|
||||
int gcryGetKeyFromProg(char *cmd, char **key, unsigned *keylen);
|
||||
rsRetVal gcryfileDeleteState(uchar *fn);
|
||||
rsRetVal gcryfileGetBytesLeftInBlock(gcryfile gf, ssize_t *left);
|
||||
|
||||
/* error states */
|
||||
#define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */
|
||||
|
||||
@ -215,7 +215,13 @@ SetDeleteOnClose(void *pF, int val)
|
||||
gcryfileSetDeleteOnClose(pF, val);
|
||||
}
|
||||
|
||||
static void
|
||||
static rsRetVal
|
||||
GetBytesLeftInBlock(void *pF, ssize_t *left)
|
||||
{
|
||||
return gcryfileGetBytesLeftInBlock((gcryfile) pF, left);
|
||||
}
|
||||
|
||||
static rsRetVal
|
||||
DeleteStateFiles(uchar *logfn)
|
||||
{
|
||||
return gcryfileDeleteState(logfn);
|
||||
@ -280,6 +286,7 @@ CODESTARTobjQueryInterface(lmcry_gcry)
|
||||
pIf->Decrypt = Decrypt;
|
||||
pIf->OnFileClose = OnFileClose;
|
||||
pIf->DeleteStateFiles = DeleteStateFiles;
|
||||
pIf->GetBytesLeftInBlock = GetBytesLeftInBlock;
|
||||
finalize_it:
|
||||
ENDobjQueryInterface(lmcry_gcry)
|
||||
|
||||
|
||||
@ -415,6 +415,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
|
||||
RS_RET_QUEUE_DISK_NO_FN = -2328,/**< disk queue configured, but filename not set */
|
||||
/* up to 2350 reserved for 7.4 */
|
||||
RS_RET_QUEUE_CRY_DISK_ONLY = -2351,/**< crypto provider only supported for disk-associated queues */
|
||||
RS_RET_NO_DATA = -2352,/**< file has no data; more a state than a real error */
|
||||
|
||||
/* RainerScript error messages (range 1000.. 1999) */
|
||||
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */
|
||||
|
||||
@ -565,6 +565,8 @@ strmReadBuf(strm_t *pThis, int *padBytes)
|
||||
int bRun;
|
||||
long iLenRead;
|
||||
size_t actualDataLen;
|
||||
size_t toRead;
|
||||
ssize_t bytesLeft;
|
||||
|
||||
ISOBJ_TYPE_assert(pThis, strm);
|
||||
/* We need to try read at least twice because we may run into EOF and need to switch files. */
|
||||
@ -575,7 +577,17 @@ strmReadBuf(strm_t *pThis, int *padBytes)
|
||||
* rgerhards, 2008-02-13
|
||||
*/
|
||||
CHKiRet(strmOpenFile(pThis));
|
||||
iLenRead = read(pThis->fd, pThis->pIOBuf, pThis->sIOBufSize);
|
||||
if(pThis->cryprov == NULL) {
|
||||
toRead = pThis->sIOBufSize;
|
||||
} else {
|
||||
CHKiRet(pThis->cryprov->GetBytesLeftInBlock(pThis->cryprovFileData, &bytesLeft));
|
||||
if(bytesLeft == -1 || bytesLeft > pThis->sIOBufSize) {
|
||||
toRead = pThis->sIOBufSize;
|
||||
} else {
|
||||
toRead = (size_t) bytesLeft;
|
||||
}
|
||||
}
|
||||
iLenRead = read(pThis->fd, pThis->pIOBuf, toRead);
|
||||
DBGOPRINT((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead);
|
||||
/* end crypto */
|
||||
if(iLenRead == 0) {
|
||||
@ -1506,6 +1518,7 @@ static rsRetVal strmSeekCurrOffs(strm_t *pThis)
|
||||
FINALIZE;
|
||||
}
|
||||
|
||||
/* As the cryprov may use CBC or similiar things, we need to read skip data */
|
||||
targetOffs = pThis->iCurrOffs;
|
||||
pThis->iCurrOffs = 0;
|
||||
dbgprintf("DDDD: skip read offs %lld, data: ", (long long) targetOffs);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user