diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/cryprov.h | 3 | ||||
-rw-r--r-- | runtime/libgcry.c | 151 | ||||
-rw-r--r-- | runtime/libgcry.h | 4 | ||||
-rw-r--r-- | runtime/lmcry_gcry.c | 9 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 | ||||
-rw-r--r-- | runtime/stream.c | 15 |
6 files changed, 146 insertions, 37 deletions
diff --git a/runtime/cryprov.h b/runtime/cryprov.h index 0c3053d4..5690904d 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -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 */ diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 0b3b8fc2..d3ac629c 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -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) { - gcry_error_t gcryError; - gcryfile gf = NULL; - uchar *iv = NULL; + off64_t blkEnd; DEFiRet; - CHKiRet(gcryfileConstruct(ctx, &gf, fname)); - gf->openMode = openMode; + 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; +} - gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo); - gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0); +/* 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; + uchar *iv = NULL; + DEFiRet; + + 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; diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 819ac77c..2f700554 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -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 */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 3941b06c..041cd581 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -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) diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index e8c2eb68..179d93e6 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -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) */ diff --git a/runtime/stream.c b/runtime/stream.c index a26f2451..5b1e105c 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -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); |