%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #if DK3_HAVE_OPENSSL_RAND_H #include #endif #if DK3_ON_WINDOWS #include #endif $(trace-include) /** Names for the PRNG types. */ static dkChar const * const dk3app_prng_type_names[] = { /* 0 */ dkT("openssl"), /* OpenSSL PRNG. */ /* 1 */ dkT("random"), /* initstate()/setstate()/random() */ /* 2 */ dkT("rand48"), /* lrand48(). */ /* 3 */ dkT("simple"), /* srand()/rand(). */ NULL }; int dk3app_get_allowed_prng_types(dk3_app_t *app, dkChar const *il) { int back = 0; dkChar buffer[256]; dkChar *p1 = NULL; dkChar *p2 = NULL; int i = 0; $? "+ dk3app_get_allowed_prng_types \"%s\"", il if((app) && (il)) { if(dk3str_len(il) < DK3_SIZEOF(buffer,dkChar)) { dk3str_cpy_not_overlapped(buffer, il); p1 = dk3str_start(buffer, NULL); while(p1) { p2 = dk3str_chr(p1, dkT(',')); if(p2) { *(p2++) = dkT('\0'); p2 = dk3str_start(p2, NULL); } i = dk3str_array_index(dk3app_prng_type_names, p1, 0); switch(i) { case 0: { #if DK3_HAVE_OPENSSL_RAND_H back |= DK3_PRNG_OPENSSL; #else /* ERROR: Openssl PRNG not available! */ dk3app_log_i1(app, DK3_LL_ERROR, 192); #endif } break; case 1: { #if DK3_HAVE_INITSTATE && DK3_HAVE_SETSTATE && DK3_HAVE_RANDOM back |= DK3_PRNG_STATE; #else /* ERROR: initstate()/setstate()/random() not available. */ dk3app_log_i1(app, DK3_LL_ERROR, 191); #endif } break; case 2: { #if DK3_HAVE_NRAND48 && DK3_HAVE_LRAND48 back |= DK3_PRNG_RAND48; #else /* ERROR: lrand48()/lrand48() not available. */ dk3app_log_i1(app, DK3_LL_ERROR, 190); #endif } break; case 3: { #if DK3_HAVE_RAND && DK3_HAVE_SRAND back |= DK3_PRNG_SIMPLE; #else /* ERROR: srand()/rand() not available! */ dk3app_log_i1(app, DK3_LL_ERROR, 189); #endif } break; default: { /* Unknown PRNG type name! */ dk3app_log_i3(app, DK3_LL_ERROR, 187, 188, p1); } break; } p1 = p2; } } else { /* ERROR: Line too long for buffer! */ dk3app_log_i1(app, DK3_LL_ERROR, 38); } } $? "- dk3app_rand_get_allowed_prng_types %d", back return back; } /** Find allowed PRNG types using preferences. @param app Application structure. @return Or-combination of DK3_PRNG_xxx. */ static int dk3app_rand_find_allowed_types(dk3_app_t *app) { int back = DK3_PRNG_ALL; dkChar buffer[256]; $? "+ dk3app_rand_find_allowed_types" if(dk3app_get_pref( app,dk3app_not_localized(37),buffer,DK3_SIZEOF(buffer,dkChar)) ) { $? ". buffer = \"%s\"", buffer back = dk3app_get_allowed_prng_types(app, buffer); } $? "- dk3app_rand_find_allowed_types %d", back return back; } /** Get bytes to seed random number generator. @param app Application structure. @param dp Destination buffer pointer. @param sz Number of bytes required. @param fn File name for seed file. @return Number of bytes found. */ static size_t dk3app_read_bytes_from_file( dk3_app_t *app, void *dp, size_t sz, dkChar const *fn ) { size_t back = 0; FILE *fipo; $? "+ dk3app_read_bytes_from_file" fipo = dk3sf_fopen_app(fn, dk3app_not_localized(36), app); if(fipo) { back = dk3sf_fread_app(dp, 1, sz, fipo, app); fclose(fipo); } $? "- dk3app_read_bytes_from_file %u", (unsigned)back return back; } /** Get bytes to seed random number generator. @param app Application structure. @param dp Destination buffer pointer. @param sz Number of bytes required. @param fn File name for seed file. @return Number of bytes found. */ static size_t dk3app_get_seed_from_source(dk3_app_t *app,void *dp,size_t sz,dkChar const *fn) { size_t back = 0; dk3_stat_t stb; int can_use_file = 1; int mode_denied; $? "+ dk3app_get_seed_from_source \"%s\"", fn /* DEBUG: Attempting to get seed data from ... */ dk3app_log_i3(app, DK3_LL_DEBUG, 196, 197, fn); if(dk3sf_stat_app(&stb, fn, NULL)) { $? ". file exists" switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_DIRECTORY: { /* Ignore it. */ } break; case DK3_FT_REGULAR: { /* Security checks before using the file. */ mode_denied = DK3_FPERM_G_READ | DK3_FPERM_G_WRITE | DK3_FPERM_O_READ | DK3_FPERM_O_WRITE; if((stb.ft) & DK3_FT_SYMLINK) { if((stb.ai) & DK3_STAT_AI_USER_DIFFERS) { can_use_file = 0; /* WARNING: Symlink owner is not file owner. */ dk3app_log_i3(app, DK3_LL_ERROR, 185, 186, fn); } if((stb.perm) & mode_denied) { can_use_file = 0; /* WARNING: File not used (permissions). */ dk3app_log_i3(app, DK3_LL_ERROR, 183, 184, fn); } else { if((stb.lperm) & mode_denied) { can_use_file = 0; /* WARNING: File not used (permissions). */ dk3app_log_i3(app, DK3_LL_ERROR, 183, 184, fn); } } } else { if((stb.perm) & mode_denied) { can_use_file = 0; /* WARNING: File not used (permissions). */ dk3app_log_i3(app, DK3_LL_ERROR, 183, 184, fn); } } if(can_use_file) { back = dk3app_read_bytes_from_file(app, dp, sz, fn); } } break; default: { /* Use file. */ back = dk3app_read_bytes_from_file(app, dp, sz, fn); } break; } } $? "- dk3app_get_seed_from_source %u", (unsigned)back /* DEBUG: Seed data: ... bytes. */ { char b1[64]; dkChar b2[64]; sprintf(b1, "%lu", (unsigned long)back); if(dk3str_cnv_c8_to_str_app(b2, DK3_SIZEOF(b2,dkChar), b1, app)) { dk3app_log_i3(app, DK3_LL_DEBUG, 198, 199, b2); } } return back; } /** Get bytes to seed random number generator. @param app Application structure. @param dp Destination buffer pointer. @param sz Number of bytes required. @param fn File name for seed file. @param passno Pass number (only 0 uses files). @return Number of bytes found. */ static size_t dk3app_get_seed_bytes( dk3_app_t *app, void *dp, size_t sz, dkChar const *fn, int passno ) { size_t back = 0; dkChar const *p1; dkChar const *p2; dkChar const *p3; dkChar const *p4; dkChar const *p5; dkChar *p6; size_t s; dkChar fnb[DK3_MAX_PATH]; /* File name buffer. */ if((app) && (dp) && (sz)) { /* $HOME/.rnd-xxx-application */ if(passno == 0) { if(dk3app_get_pref_bool(app, dk3app_not_localized(46), 1)) { p1 = dk3app_get_homedir(app); p2 = dk3app_not_localized(20); p3 = fn; p4 = dk3app_not_localized(5); p5 = dk3app_get_appname(app); if((p1) && (p2) && (p3) && (p4) && (p5)) { s = dk3str_len(p1); s += dk3str_len(p2); s += dk3str_len(p3); s += dk3str_len(p4); s += dk3str_len(p5); if(s < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(fnb, p1); dk3str_cat(fnb, p2); dk3str_cat(fnb, p3); dk3str_cat(fnb, p4); dk3str_cat(fnb, p5); back = dk3app_get_seed_from_source(app, dp, sz, fnb); } } } } /* EGD socket from environment. */ if(back == 0) { p1 = dk3sf_getenv(dk3app_not_localized(43)); if(p1) { back = dk3app_get_seed_from_source(app, dp, sz, p1); } } /* EGD socket from preferences. */ if(back == 0) { if(dk3app_get_pref( app, dk3app_not_localized(42), fnb, DK3_SIZEOF(fnb,dkChar)) ) { p6 = dk3str_start(fnb, NULL); if(p6) { dk3str_chomp(p6, NULL); back = dk3app_get_seed_from_source(app, dp, sz, p6); } } } #if !DK3_ON_WINDOWS /* /dev/urandom */ if(back == 0) { back = dk3app_get_seed_from_source(app, dp, sz, dk3app_not_localized(44)); } /* /dev/random */ if(back == 0) { back = dk3app_get_seed_from_source(app, dp, sz, dk3app_not_localized(45)); } #endif } return back; } #if DK3_HAVE_OPENSSL_RAND_H /** Initialize OpenSSL PRNG. @param app Application structure. @return 1 on success, 0 on error. */ static int dk3app_init_prng_openssl(dk3_app_t *app) { int back = 0; int cc = 0; int passno = 0; size_t sz; char seedval[DK3_SEED_OPENSSL_BYTES]; #if DK3_ON_WINDOWS BYTE sv[DK3_SEED_OPENSSL_BYTES]; HCRYPTPROV hcp; BOOL res; DWORD dwLgt; #endif $? "+ dk3app_init_prng_openssl" do { cc = 0; sz = dk3app_get_seed_bytes( app,seedval,sizeof(seedval),dk3app_not_localized(41),passno ); $? ". Number of seed bytes = %u", (unsigned)sz if(sz > 0) { cc = 1; RAND_seed(seedval, (int)sz); if(RAND_status() == 1) { cc = 0; back = 1; } } #if DK3_ON_WINDOWS if(back == 0) { $? ". obtain seed from CryptoAPI" dk3app_log_i1(app, DK3_LL_DEBUG, 227); res = CryptAcquireContext( &hcp, /* Pointer to result variable. */ NULL, /* Default container (user name). */ NULL, /* Users default cryptographic provider. */ PROV_RSA_FULL, /* Provider type. */ CRYPT_VERIFYCONTEXT ); if(res) { $? ". context ok" dwLgt = sizeof(sv); res = CryptGenRandom(hcp, dwLgt, sv); if(res) { $? ". bytes ok" cc = 1; RAND_seed(sv, sizeof(sv)); if(RAND_status() == 1) { cc = 0; back = 1; $? ". successfully seeded from CryptoAPI" } } else { $? "! no bytes" } CryptReleaseContext(hcp, (DWORD)0L); } else { $? "! no context" } } if(back == 0) { $? ". attempt to seed from screen" dk3app_log_i1(app, DK3_LL_DEBUG, 228); RAND_screen(); if(RAND_status() == 1) { cc = 0; back = 1; $? ". successfully seeded from screen" } } #endif passno++; } while(cc); if(back) { dk3app_log_i1(app, DK3_LL_DEBUG, 229); } else { /* Warning: Failed to seed OpenSSL PRNG! */ dk3app_log_i1(app, DK3_LL_ERROR, 182); } $? "- dk3app_init_prng_openssl %d", back return back; } #endif #if DK3_HAVE_INITSTATE && DK3_HAVE_SETSTATE && DK3_HAVE_RANDOM /** Initialize state PRNG. @param app Application structure. @return 1 on success, 0 on error. */ static int dk3app_init_prng_state(dk3_app_t *app) { int back = 0; unsigned int seedval = 0; if(dk3app_get_seed_bytes( app, (void *)(&seedval), sizeof(unsigned int), dk3app_not_localized(40), 0 ) ) { srandom(seedval); back = 1; } if(!back) { /* Warning: Failed to seed random() PRNG! */ dk3app_log_i1(app, DK3_LL_ERROR, 181); } return back; } #endif #if DK3_HAVE_NRAND48 && DK3_HAVE_LRAND48 /** Initialize lrand48() PRNG. @param app Application structure. @return 1 on success, 0 on error. */ static int dk3app_init_prng_rand48(dk3_app_t *app) { int back = 0; long int seedval = 0L; if(dk3app_get_seed_bytes( app, (void *)(&seedval), sizeof(long int), dk3app_not_localized(39), 0 ) ) { srand48(seedval); back = 1; } if(!back) { /*Warning: Failed to seed lrand48() PRNG! */ dk3app_log_i1(app, DK3_LL_ERROR, 180); } return back; } #endif #if DK3_HAVE_RAND && DK3_HAVE_SRAND /** Initialize simple rand() PRNG. @param app Application structure. @return 1 on success, 0 on error. */ static int dk3app_init_prng_simple(dk3_app_t *app) { int back = 0; unsigned int ui; if( dk3app_get_seed_bytes( app, (void *)(&ui), sizeof(unsigned int), dk3app_not_localized(38), 0 ) ) { srand(ui); back = 1; } if(!back) { /* Warning: Failed to seed simple PRNG! */ dk3app_log_i1(app, DK3_LL_ERROR, 179); } return back; } #endif /** Attempt to initialize a PRNG. @param app Application structure. @param il Input line listing allowed PRNGs comma-separated. @return 1 on success (at least one PRNG usable), 0 on error. */ int dk3app_rand_init(dk3_app_t *app, dkChar const *il) { int back = 0; int allowed_types; $? "+ dk3app_rand_init" if(app) { if(app->f_rand_initialized) { back = app->f_rand_success; } else { app->f_rand_initialized = 1; if(il) { allowed_types = dk3app_get_allowed_prng_types(app, il); } else { allowed_types = dk3app_rand_find_allowed_types(app); } if(!back) { if(allowed_types & DK3_PRNG_OPENSSL) { $? ". check OpenSSL" #if DK3_HAVE_OPENSSL_RAND_H $? ". OpenSSL available" back = dk3app_init_prng_openssl(app); if(back) { $? ". OpenSSL initialized" app->rand_type = DK3_PRNG_OPENSSL; } #else $? "! OpenSSL not available" #endif } } if(!back) { if(allowed_types & DK3_PRNG_STATE) { #if DK3_HAVE_INITSTATE && DK3_HAVE_SETSTATE && DK3_HAVE_RANDOM back = dk3app_init_prng_state(app); if(back) { app->rand_type = DK3_PRNG_STATE; } #endif } } if(!back) { if(allowed_types & DK3_PRNG_RAND48) { #if DK3_HAVE_NRAND48 && DK3_HAVE_LRAND48 back = dk3app_init_prng_rand48(app); if(back) { app->rand_type = DK3_PRNG_RAND48; } #endif } } if(!back) { if(allowed_types & DK3_PRNG_SIMPLE) { #if DK3_HAVE_RAND && DK3_HAVE_SRAND back = dk3app_init_prng_simple(app); if(back) { app->rand_type = DK3_PRNG_SIMPLE; } #endif } } if(back) { app->f_rand_success = 1; } } } if(!back) { /* ERROR: No PRNG usable! */ dk3app_log_i1(app, DK3_LL_ERROR, 178); } $? "- dk3app_rand_init %d", back return back; } /** Get random bytes. @param app Application structure. @param db Destination buffer. @param sz Size of \a db (number of bytes). @param f_crypto Flag: For cryptographic purposes. @return Number of bytes written to \a db. */ static size_t dk3app_rand_bytes_internal(dk3_app_t *app, void *db, size_t sz, int f_crypto) { size_t back = 0; unsigned char *ucptr = NULL; size_t i = 0; unsigned u = 0; if((app) && (db) && (sz)) { ucptr = (unsigned char *)db; if(!(app->f_rand_initialized)) { dk3app_rand_init(app, NULL); } if(app->f_rand_success) { switch(app->rand_type) { case DK3_PRNG_OPENSSL: { #if DK3_HAVE_OPENSSL_RAND_H if(f_crypto) { if(RAND_bytes(ucptr, (int)sz) == 1) { back = sz; } } else { if(RAND_pseudo_bytes(ucptr, (int)sz) > -1) { back = sz; } } #else /* BUG: Not available! */ #endif } break; case DK3_PRNG_STATE: { #if DK3_HAVE_INITSTATE && DK3_HAVE_SETSTATE && DK3_HAVE_RANDOM long lval; size_t position; lval = random() >> 8; position = 0; for(i = 0; i < sz; i++) { *(ucptr++) = (unsigned char)(lval & 0x000000FFL); position++; lval = lval >> 8; if(position >= (sizeof(long) - 1)) { lval = random() >> 8; position = 0; } } back = sz; #else /* BUG: Not available! */ #endif } break; case DK3_PRNG_RAND48: { #if DK3_HAVE_NRAND48 && DK3_HAVE_LRAND48 long lval; size_t position; lval = lrand48() >> 8; position = 0; for(i = 0; i < sz; i++) { *(ucptr++) = (unsigned char)(lval & 0x000000FFL); position++; lval = lval >> 8; if(position >= (sizeof(long) - 1)) { lval = lrand48() >> 8; position = 0; } } back = sz; #else /* BUG: Not available! */ #endif } break; default: { #if DK3_HAVE_RAND && DK3_HAVE_SRAND for(i = 0; i < sz; i++) { u = rand(); #if DK3_SIZEOF_INT > 2 #if DK3_SIZEOF_INT > 4 u = u >> 28; #else u = u >> 12; #endif #else u = u >> 4; #endif u &= 0x00FFU; *(ucptr++) = (unsigned char)u; } back = sz; #else /* BUG: Not available! */ #endif } break; } } } if(back < sz) { /* ERROR: Failed to obtain enough random bytes! */ dk3app_log_i1(app, DK3_LL_ERROR, 177); } return back; } size_t dk3app_rand_bytes(dk3_app_t *app, void *db, size_t sz) { size_t back; back = dk3app_rand_bytes_internal(app, db, sz, 1); return back; } size_t dk3app_rand_bytes_non_crypto(dk3_app_t *app, void *db, size_t sz) { size_t back; back = dk3app_rand_bytes_internal(app, db, sz, 0); return back; } void dk3app_rand_end(dk3_app_t *app) { #if DK3_HAVE_OPENSSL_RAND_H #if DK3_CHAR_SIZE == 1 dkChar const *p1 = NULL; dkChar const *p2 = NULL; dkChar const *p3 = NULL; dkChar const *p4 = NULL; dkChar const *p5 = NULL; size_t sz = 0; FILE *fipo = NULL; dkChar fnb[DK3_MAX_PATH]; #endif #endif $? "+ dk3app_rand_end" if((app->f_rand_initialized) && (app->f_rand_success)) { $? ". init" switch(app->rand_type) { case DK3_PRNG_OPENSSL: { #if DK3_HAVE_OPENSSL_RAND_H #if DK3_CHAR_SIZE == 1 p1 = dk3app_get_homedir(app); p2 = dk3app_not_localized(20); p3 = dk3app_not_localized(41); p4 = dk3app_not_localized(5); p5 = dk3app_get_appname(app); if((p1) && (p2) && (p3) && (p4) && (p5)) { sz = dk3str_len(p1); sz += dk3str_len(p2); sz += dk3str_len(p3); sz += dk3str_len(p4); sz += dk3str_len(p5); if(sz < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(fnb, p1); dk3str_cat(fnb, p2); dk3str_cat(fnb, p3); dk3str_cat(fnb, p4); dk3str_cat(fnb, p5); $? ". rand seed file = \"%s\"", fnb fipo = dk3sf_fopen_app(fnb, dk3app_not_localized(24), app); if(fipo) { fclose(fipo); fipo = NULL; #if DK3_ON_WINDOWS /* chmod(fnb, 0600); */ #else #if DK3_HAVE_CHMOD chmod(fnb, 0600); #endif #endif if(RAND_write_file(fnb) <= 0) { /* Warning: Failed to save random seed. */ dk3app_log_i1(app, DK3_LL_ERROR, 193); } } } } #endif #endif } break; } } else { $? "! not initialized" } $? "- dk3app_rand_end" } /* vim: set ai sw=2 : */