%% options copyright owner = Dirk Krause copyright year = 2011-2014 license = bsd %% module #include "dk3all.h" #include "dkt.h" $!trace-include /** Range (used for length, number of digits, number of special characters and number of upper-case characters. */ typedef struct { size_t min; /**< Minimum value. */ size_t max; /**< Maximum value. */ } DKT_PW_RANGE; /** Password class definition. */ typedef struct { dkChar const *name; /**< Password class name. */ int type; /**< Password type (DKT_PWCLASS_xxx). */ DKT_PW_RANGE lgt; /**< Length. */ DKT_PW_RANGE dig; /**< Number of digits. */ DKT_PW_RANGE spc; /**< Number of special characters. */ DKT_PW_RANGE upp; /**< Number of upper-case characters. */ } DKT_PW_CL; /** Job structure for dkt createp. */ typedef struct { dk3_app_t *app; /**< Application. */ dk3_option_set_t *opt; /**< Option set. */ dkChar const * const *msg; /**< Localized messages. */ dkChar const * const *kwnl; /**< Keywords, not localized. */ int exval; /**< Exit status code. */ dk3_sto_t *s_cl; /**< Password classes storage. */ dk3_sto_it_t *i_cl; /**< Password classes iterator. */ DKT_PW_CL pwcl; /**< Password class. */ } DKT_CRP_J; /** Options. */ static dk3_option_t const dkt_crp_options[] = { { dkT('R'), dkT("reset"), 0 }, { dkT('t'), dkT("type"), 1 }, { dkT('l'), dkT("length"), 1 }, { dkT('d'), dkT("digits"), 1 }, { dkT('s'), dkT("specials"), 1 }, { dkT('u'), dkT("upper-case"), 1 }, { dkT('c'), dkT("class"), 1 } }; /** Number of options in the dkt_crp_options array. */ static size_t const dkt_crp_szoptions = sizeof(dkt_crp_options)/sizeof(dk3_option_t); /** Long options for dkt createp. */ static dkChar const * const dkt_crp_long_options[] = { dkT("reset"), dkT("class"), NULL }; /** Keywords in a class configuration. */ static dkChar const * const dkt_crp_class_kw[] = { dkT("type"), /* 0 */ dkT("length"), /* 1 */ dkT("digits"), /* 2 */ dkT("specials"), /* 3 */ dkT("upper-case"), /* 4 */ NULL }; /** Password types. */ static dkChar const * const dkt_crp_password_types[] = { dkT("simple"), dkT("hex"), dkT("ascii-85"), dkT("h"), dkT("a85"), dkT("ascii85"), NULL }; /** Character pools for characters, digits, specials and upper-case. */ static char const * const dkt_crp_char_pools[] = { /* 0 */ "abcdefghijklmnopqrstuvwxyz", /* 1 */ "01234567890", /* 2 */ "^!\"$%&()[]{}=?\\+-*/#~_.,:;<>|", /* 3 */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ", NULL }; /** Compare two class entries. @param l Left entry. @param r Right entry. @param cr Comparison criteria (0=class/class, 1=class/name). @return Comparison result. */ static int dkt_crp_class_compare(void const *l, void const *r, int cr) { int back = 0; DKT_PW_CL const *pl = NULL; DKT_PW_CL const *pr = NULL; pl = (DKT_PW_CL const *)l; pr = (DKT_PW_CL const *)r; if(l) { if(r) { switch(cr) { case 1: { if(pl->name) { back = dk3str_cmp(pl->name, (dkChar const *)r); } else { back = -1; } } break; default: { if(pl->name) { if(pr->name) { back = dk3str_cmp(pl->name, pr->name); } else { back = 1; } } else { if(pr->name) { back = -1; } } } break; } } else { back = 1;} } else { if(r) { back = -1; } } if(back < -1) { back = -1; } if(back > 1) { back = 1; } return back; } /** Delete class entry, release memory. @param cl Class entry to delete. */ static void dkt_crp_class_delete(DKT_PW_CL *cl) { if(cl) { $? "= dkt_crp_class_delete \"%s\"", TR_STR(cl->name) dk3_release(cl->name); dk3_delete(cl); } } /** Initialize class entry. @param cl Entry to initialize. */ static void dkt_crp_class_init_empty(DKT_PW_CL *cl) { cl->name = NULL; cl->type = DKT_PWCLASS_SIMPLE; (cl->lgt).min = 6; (cl->lgt).max = 8; (cl->dig).min = 1; (cl->dig).max = 2; (cl->spc).min = 1; (cl->spc).max = 2; (cl->upp).min = 1; (cl->upp).max = 2; $? "= dkt_crp_class_init_empty" } /** Create new class entry, allocate memory. @param cn Class name. @param app Application structure for diagnostics. @return Pointer to new entry on success, NULL on error. */ static DKT_PW_CL * dkt_crp_class_new(dkChar *cn, dk3_app_t *app) { DKT_PW_CL *back = NULL; $? "+ dkt_crp_class_new \"%s\"", TR_STR(cn) if(cn) { back = dk3_new_app(DKT_PW_CL,1,app); if(back) { dkt_crp_class_init_empty(back); back->name = dk3str_dup_app(cn, app); if(!(back->name)) { dkt_crp_class_delete(back); back = NULL; } } } $? "- dkt_crp_class_new %s", TR_PTR(back) return back; } /** Set a range. @param j Job structure. @param ra Range to set up. @param v Text containing the values for range. @param verb Flag: Verbose. @return 1 on success, 0 on error. */ static int dkt_crp_set_range(DKT_CRP_J *j, DKT_PW_RANGE *ra, dkChar *v, int verb) { int back = 0; dkChar *p1 = NULL; /* Start of text. */ dkChar *p2 = NULL; /* End of range. */ unsigned u = 0; /* Used by sscanf(). */ $? "+ dkt_crp_set_range \"%s\"", TR_STR(v) p1 = dk3str_start(v, NULL); if(p1) { p2 = dk3str_chr(p1, dkT('-')); if(p2) { *(p2++) = dkT('\0'); p2 = dk3str_start(p2, NULL); } dk3str_chomp(p1, NULL); #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(p1, dkT("%u"), &u) == 1) #else if(0 != dk3ma_ui_from_string(&u, p1, NULL)) #endif { back = 1; ra->min = (size_t)u; ra->max = ra->min; if(p2) { back = 0; #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(p2, dkT("%u"), &u) == 1) #else if(0 != dk3ma_ui_from_string(&u, p2, NULL)) #endif { back = 1; ra->max = (size_t)u; } else { if(verb) { /* ERROR: Not numeric! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 141, 142, p2); } } } } else { if(verb) { /* ERROR: Not numeric! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 141, 142, p1); } } } else { if(verb) { /* ERROR: Empty interval */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 40); } } $? "- dkt_crp_set_range %d (%u-%u)", back, (unsigned)(ra->min), (unsigned)(ra->max) return back; } /** Set type for a password class. @param j job structure. @param cl Class to modify. @param v Text containing the type. @param verb Flag: Verbose. @return 1 on success, 0 on error. */ static int dkt_crp_set_type(DKT_CRP_J *j, DKT_PW_CL *cl, dkChar const *v, int verb) { int back = 0; $? "+ dkt_crp_set_type \"%s\"", TR_STR(v) switch(dk3str_array_index(dkt_crp_password_types, v, 0)) { case 0: { cl->type = DKT_PWCLASS_SIMPLE; back = 1; } break; case 1: case 3: { cl->type = DKT_PWCLASS_HEX; back = 1; } break; case 2: case 4: case 5: { cl->type = DKT_PWCLASS_A85; back = 1; } break; default: { if(verb) { /* ERROR: Unknown type! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 41, 42, v); } } break; } $? "- dkt_crp_set_type %d", back return back; } /** Apply a key/value pair to a class entry. @param j Job structure. @param cl Class to modify. @param k Key. @param v Value. @return 1 on success, 0 on error. */ static int dkt_crp_class_apply_kv(DKT_CRP_J *j, DKT_PW_CL *cl, dkChar *k, dkChar *v) { int back = 0; $? "+ dkt_crp_class_apply_kv \"%s\"=\"%s\"", TR_STR(k), TR_STR(v) switch(dk3str_array_index(dkt_crp_class_kw, k, 0)) { case 0: { back = dkt_crp_set_type(j, cl, v, 0); } break; case 1: { back = dkt_crp_set_range(j, &(cl->lgt), v, 0); } break; case 2: { back = dkt_crp_set_range(j, &(cl->dig), v, 0); } break; case 3: { back = dkt_crp_set_range(j, &(cl->spc), v, 0); } break; case 4: { back = dkt_crp_set_range(j, &(cl->upp), v, 0); } break; } $? "- dkt_crp_class_apply_kv %d", back return back; } /** Configure a class. @param j Job structure. @param cl Class to modify. @param il Input line, contains comma-separated key/value pairs. @return 1 on success, 0 on error. */ static int dkt_crp_class_configure(DKT_CRP_J *j, DKT_PW_CL *cl, dkChar *il) { int back = 1; dkChar *p1 = NULL; /* Current key/value pair. */ dkChar *p2 = NULL; /* Next key/value pair (if any). */ dkChar *p3 = NULL; /* Value pointer. */ $? "+ dkt_crp_class_configure \"%s\"", TR_STR(il) p1 = il; while(p1) { p2 = dk3str_chr(p1, dkT(',')); if(p2) { *(p2++) = dkT('\0'); p2 = dk3str_start(p2, NULL); } dk3str_chomp(p1, NULL); p3 = dk3str_chr(p1, dkT('=')); if(p3) { *(p3++) = dkT('\0'); p3 = dk3str_start(p3, NULL); if(p3) { p1 = dk3str_start(p1, NULL); if(p1) { dk3str_chomp(p1, NULL); dk3str_chomp(p3, NULL); if(!dkt_crp_class_apply_kv(j, cl, p1, p3)) { back = 0; } } else { /* ERROR: Syntax! */ back = 0; } } else { /* ERROR: Syntax! */ back = 0; } } else { /* ERROR: Syntax! */ back = 0; } p1 = p2; } $? "- dkt_crp_class_configure %d", back return back; } /** Process one line describing a class. @param j Job structure. @param il Input line to process. @return 1 on success, 0 on error. */ static int dkt_crp_class_data(DKT_CRP_J *j, dkChar const *il) { int back = 0; dkChar bu[1024]; /* Private copy. */ dkChar *p1 = NULL; /* Class name. */ dkChar *p2 = NULL; /* Start of configuration. */ DKT_PW_CL *cl = NULL; /* New class. */ $? "+ dkt_crp_class_data \"%s\"", TR_STR(il) if(dk3str_len(il) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, il); p1 = dk3str_start(bu, NULL); if(p1) { p2 = dk3str_chr(p1, dkT(',')); if(p2) { *(p2++) = dkT('\0'); p1 = dk3str_start(p1, NULL); if(p1) { dk3str_chomp(p1, NULL); cl = (DKT_PW_CL *)dk3sto_it_find_like(j->i_cl, p1, 1); if(cl) { /* Warning: Overwriting existing class. */ } else { cl = dkt_crp_class_new(p1, j->app); if(cl) { if(!dk3sto_add(j->s_cl, (void *)cl)) { dkt_crp_class_delete(cl); cl = NULL; } } } if(cl) { back = dkt_crp_class_configure(j, cl, p2); } } else { /* ERROR: Empty name! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 43); } } else { /* ERROR: No detail information! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 44); } } else { /* ERROR: No text! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 45); } } else { /* ERROR: Line too long! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 46, 47, il); } $? "- dkt_crp_class_data %d", back return back; } /** Reset job structure on -R or --reset. @param j Job structure. */ static void dkt_crp_job_reset(DKT_CRP_J *j) { int i = 0; for(i = 11; i <= 13; i++) { dkt_crp_class_data(j, (j->kwnl)[i]); } } /** Initialize job structure. @param j Job structure. @param app Application structure. */ static void dkt_crp_job_init(DKT_CRP_J *j, dk3_app_t *app) { j->app = NULL; j->msg = NULL; j->kwnl = NULL; j->opt = NULL; j->exval = DKT_RESULT_ERR_UNSPECIFIC; j->s_cl = NULL; j->i_cl = NULL; j->s_cl = dk3sto_open_app(app); dkt_crp_class_init_empty(&(j->pwcl)); if(j->s_cl) { dk3sto_set_comp(j->s_cl, dkt_crp_class_compare, 0); j->i_cl = dk3sto_it_open(j->s_cl); } $? "= dkt_crp_job_init %s %s", TR_PTR(j->s_cl), TR_PTR(j->i_cl) } /** Clean up job structure. @param j Job structure to clean up. */ static void dkt_crp_job_cleanup(DKT_CRP_J *j) { DKT_PW_CL *cl = NULL; if(j->opt) { dk3opt_close(j->opt); } j->opt = NULL; if(j->s_cl) { if(j->i_cl) { dk3sto_it_reset(j->i_cl); while((cl = (DKT_PW_CL *)dk3sto_it_next(j->i_cl)) != NULL) { dkt_crp_class_delete(cl); } dk3sto_it_close(j->i_cl); } j->i_cl = NULL; dk3sto_close(j->s_cl); } j->s_cl = NULL; $? "= dkt_crp_job_cleanup" } /** Process one key/value pair. @param jv Pointer to job structure casted to void *. @param k Key. @param v Value. @return 1 to indicate success. */ static int dkt_crp_conf_line(void *jv, dkChar const *k, dkChar const *v) { int back = 0; $? "+ dkt_crp_conf_line \"%s\"=\"%s\"", TR_STR(k), TR_STR(v) switch(dk3str_array_index(dkt_crp_long_options, v, 0)) { case 0: { /* reset */ dkt_crp_job_reset((DKT_CRP_J *)jv); back = 1; } break; case 1: { /* class */ if(v) { back = dkt_crp_class_data((DKT_CRP_J *)jv, v); } } break; } $? "- dkt_crp_conf_line %d", back return back; } /** Fill a range from command line options. @param j Job structure. @param r Range to set up. @param c Command line option char. @return 1 on success, 0 on error. */ static int dkt_crp_set_range_from_arg(DKT_CRP_J *j, DKT_PW_RANGE *r, dkChar c) { int back = 1; dkChar bu[1024]; /* Private copy of command line argument. */ dkChar const *arg = NULL; /* Command line argument. */ $? "+ dkt_crp_set_range_from_arg %c", (char)c if(dk3opt_is_set(j->opt, c)) { arg = dk3opt_get_short_arg(j->opt, c); if(arg) { if(dk3str_len(arg) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, arg); back = dkt_crp_set_range(j, r, bu, 1); if(!back) { $? "! no range" back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { $? "! too long" back = 0; j->exval = DKT_RESULT_ERR_OPTION; /* ERROR: Option too long! */ dk3app_log_i3(j->app, DK3_LL_ERROR, 135, 136, arg); } } else { $? "! no arg" back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } $? "- dkt_crp_set_range_from_arg %d", back return back; } /** Process command line arguments and options. @param j Job structure. @return 1 on success, 0 on error. */ static int dkt_crp_process_arguments(DKT_CRP_J *j) { int back = 0; int xargc = 0; /* Number of command line arguments. */ dkChar const *arg = NULL; /* Argument. */ dkChar const * const *xargv = NULL; /* Command line arguments array. */ DKT_PW_CL *cl = NULL; /* Class. */ size_t sz = 0; /* Variable to check ranges. */ xargc = dk3app_get_argc(j->app); xargv = dk3app_get_argv(j->app); xargv++; xargv++; xargc--; xargc--; $? "+ dkt_crp_process_arguments" j->opt = dk3opt_open_app( dkt_crp_options, dkt_crp_szoptions, dkT('\0'), NULL, xargc, xargv, j->app ); if(j->opt) { if(0 == dk3opt_get_error_code(j->opt)) { back = 1; if(dk3opt_is_set(j->opt, dkT('R'))) { dkt_crp_job_reset(j); } if(dk3opt_is_set(j->opt, dkT('c'))) { arg = dk3opt_get_short_arg(j->opt, dkT('c')); if(arg) { cl = (DKT_PW_CL *)dk3sto_it_find_like(j->i_cl, (void *)arg, 1); if(cl) { dk3mem_cpy((void *)(&(j->pwcl)), (void *)cl, sizeof(DKT_PW_CL)); } else { $? "! no such class" back = 0; j->exval = DKT_RESULT_ERR_OPTION; /* ERROR: No such class! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 48, 49, arg); } } else { $? "! missing c arg" back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { cl = (DKT_PW_CL *)dk3sto_it_find_like( j->i_cl, (void *)((j->kwnl)[14]), 1 ); if(cl) { dk3mem_cpy((void *)(&(j->pwcl)), (void *)cl, sizeof(DKT_PW_CL)); } else { $? "! no such class" back = 0; j->exval = DKT_RESULT_ERR_OPTION; /* ERROR: No such class! */ dk3app_log_3(j->app, DK3_LL_ERROR, j->msg, 48, 49, (j->kwnl)[14]); } } if(dk3opt_is_set(j->opt, dkT('t'))) { arg = dk3opt_get_short_arg(j->opt, dkT('t')); if(arg) { if(!dkt_crp_set_type(j, &(j->pwcl), arg, 1)) { back = 0; j->exval = DKT_RESULT_ERR_OPTION; $? "! set type" } } else { $? "! missing t arg" back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } if(!dkt_crp_set_range_from_arg(j, &((j->pwcl).lgt), dkT('l'))) { back = 0; $? "! range l" } if(!dkt_crp_set_range_from_arg(j, &((j->pwcl).dig), dkT('d'))) { back = 0; $? "! range d" } if(!dkt_crp_set_range_from_arg(j, &((j->pwcl).spc), dkT('s'))) { back = 0; $? "! range s" } if(!dkt_crp_set_range_from_arg(j, &((j->pwcl).upp), dkT('u'))) { back = 0; $? "! range u" } $? ". type = %d", (j->pwcl).type $? ". length = %u - %u", (unsigned)((j->pwcl).lgt.min), (unsigned)((j->pwcl).lgt.max) $? ". digits = %u - %u", (unsigned)((j->pwcl).dig.min), (unsigned)((j->pwcl).dig.max) $? ". special = %u - %u", (unsigned)((j->pwcl).spc.min), (unsigned)((j->pwcl).spc.max) $? ". upper = %u - %u", (unsigned)((j->pwcl).upp.min), (unsigned)((j->pwcl).upp.max) if((j->pwcl).lgt.max >= (j->pwcl).lgt.min) { if((j->pwcl).lgt.max < DKT_PASSWORD_MAXLENGTH) { if((j->pwcl).type == DKT_PWCLASS_SIMPLE) { if((j->pwcl).dig.max >= (j->pwcl).dig.min) { if((j->pwcl).spc.max >= (j->pwcl).spc.min) { if((j->pwcl).upp.max >= (j->pwcl).upp.min) { sz = (j->pwcl).upp.max + (j->pwcl).spc.max + (j->pwcl).dig.max; $? ". sz = %u", (unsigned)sz if(sz > (j->pwcl).lgt.min) { $? "! inconsistent" /* ERROR: Inconsistency! */ back = 0; j->exval = DKT_RESULT_ERR_OPTION; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 50); } } else { $? "! bad uppercase range" /* ERROR: Bad uppercase range! */ back = 0; j->exval = DKT_RESULT_ERR_OPTION; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 51); } } else { $? "! bad specials range" /* ERROR: Bad specials range! */ back = 0; j->exval = DKT_RESULT_ERR_OPTION; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 52); } } else { $? "! bad digits range" /* ERROR: Bad digits range! */ back = 0; j->exval = DKT_RESULT_ERR_OPTION; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 53); } } } else { $? "! password length too large" /* ERROR: Password length too large! */ back = 0; j->exval = DKT_RESULT_ERR_OPTION; dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 55); } } else { $? "! bad length range" /* ERROR: Bad length range! */ dk3app_log_1(j->app, DK3_LL_ERROR, j->msg, 54); back = 0; j->exval = DKT_RESULT_ERR_OPTION; } } else { j->exval = DKT_RESULT_ERR_OPTION; } } else { j->exval = DKT_RESULT_ERR_OPTION; } $? "- dkt_crp_process_arguments %d", back return back; } /** Produce ASCII-85 password. @param j Job structure. */ static void dkt_crp_run_a85(DKT_CRP_J *j) { char tb[DKT_PASSWORD_MAXLENGTH + 20]; char bb[((4 * DKT_PASSWORD_MAXLENGTH) / 5) + 6]; size_t st = 0; /* Text size for output. */ size_t sz = 0; /* Difference between min and max of range. */ size_t rb = 0; /* Number of binary random bytes to read. */ int ok = 1; /* Flag: No errors yet. */ st = (j->pwcl).lgt.min; if((j->pwcl).lgt.max > (j->pwcl).lgt.min) { sz = (j->pwcl).lgt.max - (j->pwcl).lgt.min; if(dk3app_rand_bytes(j->app, (void *)(&rb), sizeof(size_t)) == sizeof(size_t) ) { st = st + (rb % sz); } else { ok = 0; j->exval = DKT_RESULT_ERR_PRNG; } } if(ok) { sz = ((4 * st) / 5) + 1; if(dk3app_rand_bytes(j->app, (void *)bb, sz) == sz) { if(dk3enc_bin_to_ra85_app( tb, sizeof(tb), bb, sz, j->app)) { tb[st] = '\0'; dk3sf_initialize_stdout(); #if DK3_CHAR_SIZE > 1 for(rb = 0; rb < st; rb++) { dk3sf_fputc((((dkChar)(tb[rb])) & 0x00FFU), stdout); } dk3sf_fputc(dkT('\n'), stdout); #else fputs(tb, stdout); fputc('\n', stdout); #endif } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { j->exval = DKT_RESULT_ERR_PRNG; } } } /** Produce hex password. @param j Job structure. */ static void dkt_crp_run_hex(DKT_CRP_J *j) { char tb[DKT_PASSWORD_MAXLENGTH + 20]; char bb[(DKT_PASSWORD_MAXLENGTH / 2) + 5]; size_t st = 0; /* Text size for output. */ size_t sz = 0; /* Size for binary data. */ size_t rb = 0; /* Loop through result. */ int ok = 1; /* Flag: Everything ok. */ st = (j->pwcl).lgt.min; if((j->pwcl).lgt.max > (j->pwcl).lgt.min) { sz = (j->pwcl).lgt.max - (j->pwcl).lgt.min; if(dk3app_rand_bytes(j->app, (void *)(&rb), sizeof(size_t)) == sizeof(size_t) ) { st = st + (rb % sz); } else { ok = 0; j->exval = DKT_RESULT_ERR_PRNG; } } if(ok) { sz = (st / 2) + 1; if(dk3app_rand_bytes(j->app, (void *)bb, sz) == sz) { if(dk3enc_bin_to_hex_app(tb, sizeof(tb), bb, sz, j->app)) { tb[st] = '\0'; dk3sf_initialize_stdout(); #if DK3_CHAR_SIZE > 1 for(rb = 0; rb < st; rb++) { dk3sf_fputc((((dkChar)(tb[rb])) & 0x00FFU), stdout); } dk3sf_fputc(dkT('\n'), stdout); #else fputs(tb, stdout); fputc('\n', stdout); #endif } else { j->exval = DKT_RESULT_ERR_UNSPECIFIC; } } else { j->exval = DKT_RESULT_ERR_PRNG; } } } /** Choose a random value from a range. @param j Job structure. @param r Range. @param ok Pointer to OK-flag variable. @return Value from range. */ static size_t dkt_crp_get_value_from_range(DKT_CRP_J *j, DKT_PW_RANGE *r, int *ok) { size_t back = 0; size_t val = 0; back = r->min; if((r->max) > (r->min)) { if(dk3app_rand_bytes(j->app, (void *)(&val), sizeof(size_t)) == sizeof(size_t) ) { back = back + (val % ((r->max) - (r->min))); } else { *ok = 0; /* ERROR: Failed to obtain random bytes! */ j->exval = DKT_RESULT_ERR_PRNG; } } return back; } /** Set entries in the type character array. @param j Job structure. @param ty Type character array. @param in Index array. @param kc Key character to set. @param np Number of positions to set. @param pa Positions available. @param ok Pointer to ok-flag variable. */ static void dkt_crp_ct( DKT_CRP_J *j, char *ty, size_t *in, char kc, size_t np, size_t *pa, int *ok ) { size_t ri = 0; /* Random index into the in[0...*pa] array. */ size_t i = 0; /* Pass number, 0 ... (np-1). */ size_t k = 0; /* Index of current index to copy. */ size_t numcopy = 0; /* Number of indexes to copy. */ $? "+ dkt_crp_ct" for(i = 0; ((i < np) && ((*pa) > 0) && (*ok)); i++) { $? ". pass %u/%u positions %u", (unsigned)i, (unsigned)np, (unsigned)(*pa) if(dk3app_rand_bytes(j->app,(void *)(&ri),sizeof(size_t)) == sizeof(size_t)) { ri = (ri % (*pa)); $? ". ri = %u", (unsigned)ri ty[ in[ri] ] = kc; $? ". index = %u", (unsigned)(in[ri]) numcopy = (*pa) - ri - 1; $? ". numcopy = %u", (unsigned)numcopy if(numcopy > 0) { for(k = 0; k < numcopy; k++) { in[ri + k] = in[ri + k + 1]; } } *pa = *pa - 1; $? ". pa = %u", (unsigned)(*pa) } else { *ok = 0; j->exval = DKT_RESULT_ERR_PRNG; } } $? "- dkt_crp_ct" } /** Find one character of a given type. @param j Job structure. @param bu Buffer to fill. @param ty Buffer of type indicators already filled. @param i Index of character in bu to fill. @param ok Pointer to OK-flag variable. */ static void dkt_crp_find_char(DKT_CRP_J *j, char *bu, char *ty, size_t i, int *ok) { char const *poolptr = NULL; /* Pointer to character pool string. */ size_t poollgt = 0; /* Length of the pool string. */ size_t ri = 0; /* Random index into pool string. */ $? ". dkt_crp_find_char %u", (unsigned) i poolptr = dkt_crp_char_pools[0]; $? ". type = %x", ty[i] switch(ty[i]) { case 0x01: { poolptr = dkt_crp_char_pools[1]; } break; case 0x02: { poolptr = dkt_crp_char_pools[2]; } break; case 0x03: { poolptr = dkt_crp_char_pools[3]; } break; } poollgt = dk3str_c8_len(poolptr); if(dk3app_rand_bytes(j->app, (void *)(&ri), sizeof(size_t)) == sizeof(size_t)) { ri = (ri % poollgt); bu[i] = poolptr[ri]; $? ". chr = \"%c\" (%x)", poolptr[ri], poolptr[ri] } else { *ok = 0; j->exval = DKT_RESULT_ERR_PRNG; } $? "- dkt_crp_find_char" } /** Produce a simple password. @param j Job structure. */ static void dkt_crp_run_simple(DKT_CRP_J *j) { size_t st = 0; /* Text size for output. */ size_t sd = 0; /* Number of digits. */ size_t ss = 0; /* Number of special characters. */ size_t su = 0; /* Number of upper-case characters. */ size_t posav = 0; /* Number of positions available. */ int ok = 1; /* Flag: Everything ok. */ char bu[DKT_PASSWORD_MAXLENGTH]; char ty[DKT_PASSWORD_MAXLENGTH]; size_t in[DKT_PASSWORD_MAXLENGTH]; size_t i = 0; /* Index of current output character. */ /* Find length values. */ st = dkt_crp_get_value_from_range(j, &((j->pwcl).lgt), &ok); sd = dkt_crp_get_value_from_range(j, &((j->pwcl).dig), &ok); ss = dkt_crp_get_value_from_range(j, &((j->pwcl).spc), &ok); su = dkt_crp_get_value_from_range(j, &((j->pwcl).upp), &ok); posav = st; if(ok) { /* Initialize buffers. */ for(i = 0; i < DKT_PASSWORD_MAXLENGTH; i++) { bu[i] = '\0'; ty[i] = '\0'; in[i] = i; } /* Find character types. */ dkt_crp_ct(j, ty, in, 0x01, sd, &posav, &ok); dkt_crp_ct(j, ty, in, 0x02, ss, &posav, &ok); dkt_crp_ct(j, ty, in, 0x03, su, &posav, &ok); if(ok) { /* Get the random characters. */ for(i = 0; i < st; i++) { dkt_crp_find_char(j, bu, ty, i, &ok); } bu[st] = '\0'; if(ok) { /* Write output. */ dk3sf_initialize_stdout(); #if DK3_CHAR_SIZE > 1 for(i = 0; i < st; i++) { dk3sf_fputc((dkChar)(bu[i]), stdout); } dk3sf_fputc(dkT('\n'), stdout); #else fputs(bu, stdout); fputc('\n', stdout); #endif } } else { } } else { } } /** Create password. @param j Job structure. */ static void dkt_crp_run(DKT_CRP_J *j) { $? "+ dkt_crp_run" if(dk3app_rand_init(j->app, NULL)) { switch((j->pwcl).type) { case DKT_PWCLASS_A85: { dkt_crp_run_a85(j); } break; case DKT_PWCLASS_HEX: { dkt_crp_run_hex(j); } break; default: { dkt_crp_run_simple(j); } break; } dk3app_rand_end(j->app); } else { j->exval = DKT_RESULT_ERR_PRNG; } $? "- dkt_crp_run" } int dkt_crp( dk3_app_t *app, dkChar const *sn, dkChar const * const *msg, dkChar const * const *kwnl ) { int back = DKT_RESULT_ERR_UNSPECIFIC; int ok = 1; int i; DKT_CRP_J j; $? "+ dkt_crp" dkt_crp_job_init(&j, app); j.app = app; j.msg = msg; j.kwnl = kwnl; if((j.s_cl) && (j.i_cl)) { for(i = 11; i <= 13; i++) { if(!dkt_crp_class_data(&j, kwnl[i])) { ok = 0; } } if(ok) { dkt_tool_read_conf(app, sn, (void *)(&j), dkt_crp_conf_line); if(dkt_crp_process_arguments(&j)) { j.exval = DKT_RESULT_OK; dkt_crp_run(&j); } } } back = j.exval; dkt_crp_job_cleanup(&j); $? "- dkt_crp %d", back return back; } /* vim: set ai sw=2 : */