diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2013-05-13 08:04:13 +0200 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2013-05-13 08:04:13 +0200 |
commit | 0d000a8b1096abb26f9e47a4083dc560fed0282d (patch) | |
tree | 5f62c0e9bc51fc63c65b50de3942754e3420376f | |
parent | 415b26d5a19d8b1fd50d8e0b7b29cc8527537316 (diff) | |
download | rsyslog-0d000a8b1096abb26f9e47a4083dc560fed0282d.tar.gz rsyslog-0d000a8b1096abb26f9e47a4083dc560fed0282d.tar.bz2 rsyslog-0d000a8b1096abb26f9e47a4083dc560fed0282d.zip |
basic queue file encryption
-rw-r--r-- | runtime/cryprov.h | 5 | ||||
-rw-r--r-- | runtime/libgcry.c | 202 | ||||
-rw-r--r-- | runtime/libgcry.h | 10 | ||||
-rw-r--r-- | runtime/lmcry_gcry.c | 16 | ||||
-rw-r--r-- | runtime/queue.c | 27 | ||||
-rw-r--r-- | runtime/queue.h | 2 | ||||
-rw-r--r-- | runtime/stream.c | 8 | ||||
-rw-r--r-- | tools/omfile.c | 1 |
8 files changed, 248 insertions, 23 deletions
diff --git a/runtime/cryprov.h b/runtime/cryprov.h index cbb2f45d..66c1cfd1 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -38,9 +38,10 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Construct)(void *ppThis); rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst, int paramType); rsRetVal (*Destruct)(void *ppThis); - rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); + rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData, char openMode); 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); ENDinterface(cryprov) -#define cryprovCURR_IF_VERSION 2 /* increment whenever you change the interface structure! */ +#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 51c10af4..3fca50ec 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -49,8 +49,10 @@ #include <errno.h> #include "rsyslog.h" +#include "srUtils.h" #include "libgcry.h" +#define READBUF_SIZE 4096 /* size of the read buffer */ static rsRetVal eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf) @@ -90,19 +92,66 @@ finalize_it: RETiRet; } +static rsRetVal +eiRead(gcryfile gf) +{ + ssize_t nRead; + DEFiRet; + + if(gf->readBuf == NULL) { + CHKmalloc(gf->readBuf = malloc(READBUF_SIZE)); + } + + nRead = read(gf->fd, gf->readBuf, READBUF_SIZE); + if(nRead <= 0) { /* TODO: provide specific EOF case? */ + ABORT_FINALIZE(RS_RET_ERR); + } + gf->readBufMaxIdx = (int16_t) nRead; + gf->readBufIdx = 0; + +finalize_it: + RETiRet; +} + + +/* returns EOF on any kind of error */ +static int +eiReadChar(gcryfile gf) +{ + int c; + + if(gf->readBufIdx >= gf->readBufMaxIdx) { + if(eiRead(gf) != RS_RET_OK) { + c = EOF; + goto finalize_it; + } + } + c = gf->readBuf[gf->readBufIdx++]; +finalize_it: + return c; +} + static rsRetVal eiCheckFiletype(gcryfile gf) { char hdrBuf[128]; size_t toRead, didRead; + sbool bNeedClose = 0; DEFiRet; - CHKiRet(eiOpenRead(gf)); + if(gf->fd == -1) { + bNeedClose = 1; + CHKiRet(eiOpenRead(gf)); + } + if(Debug) memset(hdrBuf, 0, sizeof(hdrBuf)); /* for dbgprintf below! */ toRead = sizeof("FILETYPE:")-1 + sizeof(RSGCRY_FILETYPE_NAME)-1 + 1; didRead = read(gf->fd, hdrBuf, toRead); - close(gf->fd); + if(bNeedClose) { + close(gf->fd); + gf->fd = -1; + } DBGPRINTF("eiCheckFiletype read %d bytes: '%s'\n", didRead, hdrBuf); if( didRead != toRead || strncmp(hdrBuf, "FILETYPE:" RSGCRY_FILETYPE_NAME "\n", toRead)) @@ -111,6 +160,79 @@ finalize_it: RETiRet; } +/* rectype/value must be EIF_MAX_*_LEN+1 long! + * returns 0 on success or something else on error/EOF + */ +static rsRetVal +eiGetRecord(gcryfile gf, char *rectype, char *value) +{ + unsigned short i, j; + int c; + DEFiRet; + + for(i = 0 ; i < EIF_MAX_RECTYPE_LEN ; ++i) { + c = eiReadChar(gf); + if(c == ':' || c == EOF) + break; + rectype[i] = c; + } + if(c != ':') { ABORT_FINALIZE(RS_RET_ERR); } + rectype[i] = '\0'; + j = 0; + for(++i ; i < EIF_MAX_VALUE_LEN ; ++i, ++j) { + c = eiReadChar(gf); + if(c == '\n' || c == EOF) + break; + value[j] = c; + } + if(c != '\n') { ABORT_FINALIZE(RS_RET_ERR); } + value[j] = '\0'; +finalize_it: + RETiRet; +} + +static rsRetVal +eiGetIV(gcryfile gf, uchar *iv, size_t leniv) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + size_t valueLen; + unsigned short i, j; + unsigned char nibble; + DEFiRet; + + CHKiRet(eiGetRecord(gf, rectype, value)); + if(strcmp(rectype, "IV")) { + DBGPRINTF("no IV record found when expected, record type " + "seen is '%s'\n", rectype); + ABORT_FINALIZE(RS_RET_ERR); + } + valueLen = strlen(value); + if(valueLen/2 != leniv) { + DBGPRINTF("length of IV is %d, expected %d\n", + valueLen/2, leniv); + ABORT_FINALIZE(RS_RET_ERR); + } + + for(i = j = 0 ; i < valueLen ; ++i) { + if(value[i] >= '0' && value[i] <= '9') + nibble = value[i] - '0'; + else if(value[i] >= 'a' && value[i] <= 'f') + nibble = value[i] - 'a' + 10; + else { + DBGPRINTF("invalid IV '%s'\n", value); + ABORT_FINALIZE(RS_RET_ERR); + } + if(i % 2 == 0) + iv[j] = nibble << 4; + else + iv[j++] |= nibble; + } +finalize_it: + RETiRet; +} + + static rsRetVal eiOpenAppend(gcryfile gf) { @@ -180,7 +302,9 @@ eiClose(gcryfile gf, off64_t offsLogfile) /* 2^64 is 20 digits, so the snprintf buffer is large enough */ len = snprintf(offs, sizeof(offs), "%lld", offsLogfile); eiWriteRec(gf, "END:", 4, offs, len); + free(gf->readBuf); close(gf->fd); + gf->fd = -1; DBGPRINTF("encryption info file %s: closed\n", gf->eiName); } @@ -193,6 +317,7 @@ gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn) CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s))); gf->ctx = ctx; + gf->fd = -1; snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX); fn[MAXFNAME] = '\0'; /* be on save side */ gf->eiName = (uchar*) strdup(fn); @@ -246,13 +371,13 @@ addPadding(gcryfile pF, uchar *buf, size_t *plen) } static inline void -removePadding(char *buf, size_t *plen) +removePadding(uchar *buf, size_t *plen) { unsigned len = (unsigned) *plen; unsigned iSrc, iDst; - char *frstNUL; + uchar *frstNUL; - frstNUL = strchr(buf, 0x00); + frstNUL = (uchar*)strchr((char*)buf, 0x00); if(frstNUL == NULL) goto done; iDst = iSrc = frstNUL - buf; @@ -343,8 +468,31 @@ seedIV(gcryfile gf, uchar **iv) } } +static inline rsRetVal +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); + } + } while(localRet != RS_RET_OK); + 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) +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode) { gcry_error_t gcryError; gcryfile gf = NULL; @@ -352,6 +500,7 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname) DEFiRet; CHKiRet(gcryfileConstruct(ctx, &gf, fname)); + gf->mode = openMode; gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo); @@ -371,7 +520,12 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname) ABORT_FINALIZE(RS_RET_ERR); } - seedIV(gf, &iv); + if(openMode == 'r') { + readIV(gf, &iv); + } else { + seedIV(gf, &iv); + } + gcryError = gcry_cipher_setiv(gf->chd, iv, gf->blkLength); if (gcryError) { dbgprintf("gcry_cipher_setiv failed: %s/%s\n", @@ -379,8 +533,10 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname) gcry_strerror(gcryError)); ABORT_FINALIZE(RS_RET_ERR); } - CHKiRet(eiOpenAppend(gf)); - CHKiRet(eiWriteIV(gf, iv)); + if(openMode == 'w') { + CHKiRet(eiOpenAppend(gf)); + CHKiRet(eiWriteIV(gf, iv)); + } *pgf = gf; finalize_it: free(iv); @@ -389,7 +545,7 @@ finalize_it: RETiRet; } -int +rsRetVal rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len) { int gcryError; @@ -410,6 +566,32 @@ finalize_it: RETiRet; } +/* TODO: handle multiple blocks + * test-read END record; if present, store offset, else unbounded (current active block) + * when decrypting, check if bound is reached. If yes, split into two blocks, get new IV for + * second one. + */ +rsRetVal +rsgcryDecrypt(gcryfile pF, uchar *buf, size_t *len) +{ + gcry_error_t gcryError; + DEFiRet; + + gcryError = gcry_cipher_decrypt(pF->chd, buf, *len, NULL, 0); + if(gcryError) { + DBGPRINTF("gcry_cipher_decrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + removePadding(buf, len); +dbgprintf("DDDD: decrypted, buffer is now '%50.50s'\n", buf); + +finalize_it: + RETiRet; +} + + /* module-init dummy for potential later use */ int diff --git a/runtime/libgcry.h b/runtime/libgcry.h index b77b0f9e..190f4737 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -38,7 +38,11 @@ struct gcryfile_s { size_t blkLength; /* size of low-level crypto block */ uchar *eiName; /* name of .encinfo file */ int fd; /* descriptor of .encinfo file (-1 if not open) */ + char mode; /* 'r': read, 'w': write */ gcryctx ctx; + uchar *readBuf; + int16_t readBufIdx; + int16_t readBufMaxIdx; }; int gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen); @@ -50,8 +54,10 @@ rsRetVal rsgcrySetAlgo(gcryctx ctx, uchar *modename); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); int gcryfileDestruct(gcryfile gf, off64_t offsLogfile); -rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname); -int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname, char openMode); +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); /* error states */ #define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 31fab0d4..decb8591 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -211,13 +211,14 @@ finalize_it: static rsRetVal -OnFileOpen(void *pT, uchar *fn, void *pGF) +OnFileOpen(void *pT, uchar *fn, void *pGF, char openMode) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; gcryfile *pgf = (gcryfile*) pGF; DEFiRet; +dbgprintf("DDDD: open file '%s', mode '%c'\n", fn, openMode); - CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn)); + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn, openMode)); finalize_it: /* TODO: enable this error message (need to cleanup loop first ;)) errmsg.LogError(0, iRet, "Encryption Provider" @@ -227,6 +228,16 @@ finalize_it: } static rsRetVal +Decrypt(void *pF, uchar *rec, size_t *lenRec) +{ + DEFiRet; + iRet = rsgcryDecrypt(pF, rec, lenRec); + + RETiRet; +} + + +static rsRetVal Encrypt(void *pF, uchar *rec, size_t *lenRec) { DEFiRet; @@ -254,6 +265,7 @@ CODESTARTobjQueryInterface(lmcry_gcry) pIf->Destruct = (rsRetVal(*)(void*)) lmcry_gcryDestruct; pIf->OnFileOpen = OnFileOpen; pIf->Encrypt = Encrypt; + pIf->Decrypt = Decrypt; pIf->OnFileClose = OnFileClose; finalize_it: ENDobjQueryInterface(lmcry_gcry) diff --git a/runtime/queue.c b/runtime/queue.c index 87f5819e..6af4905b 100644 --- a/runtime/queue.c +++ b/runtime/queue.c @@ -835,6 +835,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis) CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pWrite, 10000000)); CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pWrite, STREAMMODE_WRITE)); CHKiRet(strm.SetsType(pThis->tVars.disk.pWrite, STREAMTYPE_FILE_CIRCULAR)); + if(pThis->useCryprov) { + CHKiRet(strm.Setcryprov(pThis->tVars.disk.pWrite, &pThis->cryprov)); + CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pWrite, pThis->cryprovData)); + } CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pWrite)); CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDeq)); @@ -843,6 +847,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis) CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDeq, 10000000)); CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDeq, STREAMMODE_READ)); CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDeq, STREAMTYPE_FILE_CIRCULAR)); + if(pThis->useCryprov) { + CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDeq, &pThis->cryprov)); + CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDeq, pThis->cryprovData)); + } CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDeq)); CHKiRet(strm.Construct(&pThis->tVars.disk.pReadDel)); @@ -852,6 +860,10 @@ static rsRetVal qConstructDisk(qqueue_t *pThis) CHKiRet(strm.SetiMaxFiles(pThis->tVars.disk.pReadDel, 10000000)); CHKiRet(strm.SettOperationsMode(pThis->tVars.disk.pReadDel, STREAMMODE_READ)); CHKiRet(strm.SetsType(pThis->tVars.disk.pReadDel, STREAMTYPE_FILE_CIRCULAR)); + if(pThis->useCryprov) { + CHKiRet(strm.Setcryprov(pThis->tVars.disk.pReadDel, &pThis->cryprov)); + CHKiRet(strm.SetcryprovData(pThis->tVars.disk.pReadDel, pThis->cryprovData)); + } CHKiRet(strm.ConstructFinalize(pThis->tVars.disk.pReadDel)); CHKiRet(strm.SetFName(pThis->tVars.disk.pWrite, pThis->pszFilePrefix, pThis->lenFilePrefix)); @@ -1321,6 +1333,7 @@ rsRetVal qqueueConstruct(qqueue_t **ppThis, queueType_t qType, int iWorkerThread pThis->iMaxFileSize = 1024 * 1024; /* default is 1 MiB */ pThis->iQueueSize = 0; pThis->nLogDeq = 0; + pThis->useCryprov = 0; pThis->iMaxQueueSize = iMaxQueueSize; pThis->pConsumer = pConsumer; pThis->iNumWorkerThreads = iWorkerThreads; @@ -2390,7 +2403,13 @@ CODESTARTobjDestruct(qqueue) free(pThis->pszFilePrefix); free(pThis->pszSpoolDir); - free(pThis->cryprovName); + if(pThis->useCryprov) { + pThis->cryprov.Destruct(&pThis->cryprovData); + obj.ReleaseObj(__FILE__, pThis->cryprovNameFull+2, pThis->cryprovNameFull, + (void*) &pThis->cryprov); + free(pThis->cryprovName); + free(pThis->cryprovNameFull); + } /* some queues do not provide stats and thus have no statsobj! */ if(pThis->statsobj != NULL) @@ -2700,7 +2719,7 @@ initCryprov(qqueue_t *pThis, struct nvlst *lst) if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pThis->cryprovName) == sizeof(szDrvrName)) { - errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider " + errmsg.LogError(0, RS_RET_ERR, "queue: crypto provider " "name is too long: '%s' - encryption disabled", pThis->cryprovName); ABORT_FINALIZE(RS_RET_ERR); @@ -2715,14 +2734,14 @@ initCryprov(qqueue_t *pThis, struct nvlst *lst) */ if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pThis->cryprov) != RS_RET_OK) { - errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " + errmsg.LogError(0, RS_RET_LOAD_ERROR, "queue: could not load " "crypto provider '%s' - encryption disabled", szDrvrName); ABORT_FINALIZE(RS_RET_CRYPROV_ERR); } if(pThis->cryprov.Construct(&pThis->cryprovData) != RS_RET_OK) { - errmsg.LogError(0, RS_RET_CRYPROV_ERR, "omfile: error constructing " + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "queue: error constructing " "crypto provider %s dataset - encryption disabled", szDrvrName); ABORT_FINALIZE(RS_RET_CRYPROV_ERR); diff --git a/runtime/queue.h b/runtime/queue.h index 9ed7f87d..844523ad 100644 --- a/runtime/queue.h +++ b/runtime/queue.h @@ -172,8 +172,8 @@ struct queue_s { sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ uchar *cryprovName; /* crypto provider to use */ cryprov_if_t cryprov; /* ptr to crypto provider interface */ + void *cryprovData; /* opaque data ptr for provider use */ uchar *cryprovNameFull;/* full internal crypto provider name */ - void *cryprovData; /* opaque data ptr for provider use */ DEF_ATOMIC_HELPER_MUT(mutQueueSize); DEF_ATOMIC_HELPER_MUT(mutLogDeq); /* for statistics subsystem */ diff --git a/runtime/stream.c b/runtime/stream.c index b781324a..e411577f 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -257,7 +257,8 @@ doPhysOpen(strm_t *pThis) if(pThis->cryprov != NULL) { CHKiRet(pThis->cryprov->OnFileOpen(pThis->cryprovData, - pThis->pszCurrFName, &pThis->cryprovFileData)); + pThis->pszCurrFName, &pThis->cryprovFileData, + (pThis->tOperationsMode == STREAMMODE_READ) ? 'r' : 'w')); } finalize_it: RETiRet; @@ -568,11 +569,16 @@ strmReadBuf(strm_t *pThis) CHKiRet(strmOpenFile(pThis)); iLenRead = read(pThis->fd, pThis->pIOBuf, pThis->sIOBufSize); DBGOPRINT((obj_t*) pThis, "file %d read %ld bytes\n", pThis->fd, iLenRead); + /* end crypto */ if(iLenRead == 0) { CHKiRet(strmHandleEOF(pThis)); } else if(iLenRead < 0) ABORT_FINALIZE(RS_RET_IO_ERROR); else { /* good read */ + /* here we place our crypto interface */ + if(pThis->cryprov != NULL) { + pThis->cryprov->Decrypt(pThis->cryprovFileData, pThis->pIOBuf, &iLenRead); + } pThis->iBufPtrMax = iLenRead; bRun = 0; /* exit loop */ } diff --git a/tools/omfile.c b/tools/omfile.c index 1740e8bf..2ebb7df9 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -155,7 +155,6 @@ typedef struct _instanceData { uchar *cryprovName; /* crypto provider */ uchar *cryprovNameFull;/* full internal crypto provider name */ void *cryprovData; /* opaque data ptr for provider use */ - void *cryprovFileData;/* opaque data ptr for file instance */ cryprov_if_t cryprov; /* ptr to crypto provider interface */ sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ int iCurrElt; /* currently active cache element (-1 = none) */ |