1 /*
2  * One-time password generator
3  *
4  * Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/>
5  */
6 
7 #define _GNU_SOURCE
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <pwd.h>
13 #include <time.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <termios.h>
20 #include <assert.h>
21 #include <termios.h>
22 #include <limits.h>
23 #include "otpw.h"
24 
25 
26 #define NL "\r\n"               /* new line sequence in password list output */
27 #define FF "\f\n"              /* form feed sequence in password list output */
28 #define MAX_PASSWORDS 1000                /* maximum length of password list */
29 #define MASTERKEY_CHECKBITS 4          /* error-detection bits in master key */
30 
31 /* shell commands that provide high entropy output for RNG */
32 char *entropy_cmds[] = {
33   "head -c 20 /dev/urandom 2>&1",
34   "ls -lu /etc/. /tmp/. / /usr/. /bin/. /usr/bin/.",
35   "PATH=/usr/ucb:/bin:/usr/bin;ps lax",
36   "last | head -50",
37   "uptime;netstat -n;hostname;date;w",
38   "cd $HOME; cat .pgp/randseed.bin .ssh/random_seed .otpw 2>&1"
39   /* too slow: "PATH=/usr/bin/X11/;xwd -root -silent 2>&1||xwd -root 2>&1" */
40 };
41 
42 /* Environment variable settings for entropy_cmds */
43 char *entropy_env[] = {
44   "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc:/usr/etc:/usr/ucb"
45 };
46 
47 /* Minimum password entropy [bits] permitted by otpw-gen (option -e) */
48 int emin=30;
49 
50 /* suffix added to temporary OTPW file */
51 char *tmpsuffix  = ".tmp";
52 
53 /*
54  * A list of common English four letter words. It has not been checked
55  * particularly well for being free of rude words or trademarks; users
56  * are meant to keep them secret anyway.
57  */
58 
59 char word[2048][4] = {
60   "abel","able","ably","acer","aces","acet","ache","acid","acne","acre",
61   "acts","adam","adds","aden","afar","aged","ages","aide","aids","aims",
62   "airs","airy","ajar","akin","alan","alas","alec","ales","alex","alix",
63   "ally","alma","alps","also","alto","amen","ames","amid","amis","amos",
64   "amps","anal","andy","anew","ange","angy","anna","anne","ante","anti",
65   "ants","anus","anya","aoun","apes","apex","apse","arab","arch","arcs",
66   "ards","area","aria","arid","arms","army","arts","asda","asia","asks",
67   "atom","atop","audi","aung","aunt","aura","auto","avid","aviv","avon",
68   "away","awry","axed","axes","axis","axle","aziz","baba","babe","baby",
69   "bach","back","bade","bags","bail","bait","bake","baku","bald","bale",
70   "bali","ball","balm","band","bang","bank","bans","bare","bark","barn",
71   "barr","bars","bart","base","bash","bass","bath","bats","bays","bcci",
72   "bdda","bead","beak","beam","bean","bear","beat","beck","bede","beds",
73   "beef","been","beep","beer","bees","begs","bell","belt","bend","benn",
74   "bent","berg","bert","best","beta","beth","bets","bias","bids","biff",
75   "bike","bile","bill","bind","bins","bird","birk","birt","bite","bits",
76   "blah","blew","blip","blob","bloc","blot","blow","blue","blur","boar",
77   "boat","bodo","body","boer","bogs","boil","bold","bolt","bomb","bond",
78   "bone","bonn","bono","bony","book","boom","boon","boot","bore","borg",
79   "born","boro","boss","both","bout","bowe","bowl","bows","boyd","boys",
80   "brad","bran","bras","brat","bray","bred","brew","brim","brom","bros",
81   "brow","buck","buds","buff","bugs","bulb","bulk","bull","bump","bums",
82   "bunk","buns","buoy","burn","burr","burt","bury","bush","bust","busy",
83   "butt","buys","buzz","byre","byte","cabs","cafe","cage","cain","cake",
84   "calf","call","calm","came","camp","cane","cans","cape","caps","capt",
85   "cara","card","care","carl","caro","carp","carr","cars","cart","casa",
86   "case","cash","cask","cast","cats","cave","cdna","cegb","cell","cent",
87   "cert","chad","chan","chap","chas","chat","chef","chen","cher","chew",
88   "chic","chin","chip","chop","chub","chum","cite","city","clad","clan",
89   "claw","clay","cleo","clio","clip","club","clue","cnaa","cnut","coal",
90   "coat","coax","coca","code","cohn","coil","coin","coke","cola","cold",
91   "cole","coli","colt","coma","comb","come","comp","cone","cons","cook",
92   "cool","cope","cops","copy","cord","core","cork","corn","corp","cose",
93   "cost","cosy","cots","coun","coup","cove","cows","cpre","cpsu","cpus",
94   "crab","crag","crap","cray","creb","crew","crim","crop","crow","crux",
95   "cruz","csce","cuba","cube","cubs","cues","cuff","cult","cunt","cups",
96   "curb","curd","cure","curl","curt","cute","cuts","daak","dada","dads",
97   "daft","dahl","dais","dale","daly","dame","damn","damp","dams","dana",
98   "dane","dank","dare","dark","dart","dash","data","date","dave","davy",
99   "dawn","days","daze","dead","deaf","deal","dean","dear","debt","deck",
100   "deed","deep","deer","deft","defy","dell","demo","deng","dent","deny",
101   "dept","desk","dial","dice","dick","died","dies","diet","digs","dine",
102   "ding","dino","dint","dire","dirk","dirt","disc","dish","disk","dive",
103   "dock","dodd","does","dogs","dole","doll","dome","done","dons","doom",
104   "door","dope","dora","dose","doth","dots","doug","dour","dove","dowd",
105   "down","drab","drag","draw","drew","drip","drop","drug","drum","dual",
106   "duck","duct","duel","dues","duet","duff","duke","dull","duly","duma",
107   "dumb","dump","dune","dung","dunn","dusk","dust","duty","dyer","dyes",
108   "dyke","each","earl","earn","ears","ease","east","easy","eats","echo",
109   "ecsc","eddy","eden","edge","edgy","edie","edit","edna","edta","eels",
110   "efta","egan","eggs","egon","egos","eire","ella","else","emil","emit",
111   "emma","ends","enid","envy","epic","ercp","eric","erik","esau","esrc",
112   "esso","eton","euro","evan","even","ever","evil","ewen","ewes","exam",
113   "exit","exon","expo","eyed","eyes","eyre","ezra","face","fact","fade",
114   "fads","fags","fail","fair","fake","fall","fame","fand","fans","fare",
115   "farm","farr","fast","fate","fats","fawn","faye","fear","feat","feed",
116   "feel","fees","feet","fell","felt","fend","fenn","fens","fern","fete",
117   "feud","fiat","fife","figs","fiji","file","fill","film","find","fine",
118   "finn","fins","fire","firm","fish","fist","fits","five","flag","flak",
119   "flap","flat","flaw","flea","fled","flee","flew","flex","flip","flop",
120   "flow","floy","flue","flux","foal","foam","foci","foes","foil","fold",
121   "folk","fond","font","food","fool","foot","ford","fore","fork","form",
122   "fort","foul","four","fowl","fran","frau","fray","fred","free","fret",
123   "frog","from","ftse","fuel","fuji","full","fund","funk","furs","fury",
124   "fuse","fuss","fyfe","gael","gail","gain","gait","gala","gale","gall",
125   "game","gang","gaol","gaps","garb","gary","gash","gasp","gate","gatt",
126   "gaul","gave","gays","gaza","gaze","gcse","gear","gels","gems","gene",
127   "gens","gent","germ","gets","gift","gigs","gill","gilt","gina","girl",
128   "gist","give","glad","glee","glen","glow","glue","glum","goal","goat",
129   "gods","goes","goff","gogh","gold","golf","gone","good","gore","gory",
130   "gosh","gown","grab","graf","gram","gran","gray","greg","grew","grey",
131   "grid","grim","grin","grip","grit","grow","grub","guil","gulf","gull",
132   "gulp","gums","gunn","guns","guru","gust","guts","guys","gwen","hack",
133   "haig","hail","hair","hale","half","hall","halo","halt","hams","hand",
134   "hang","hank","hans","hard","hare","hari","harm","harp","hart","hash",
135   "hate","hath","hats","hatt","haul","have","hawk","haze","hazy","head",
136   "heal","heap","hear","heat","heck","heed","heel","heir","hela","held",
137   "hell","helm","help","hens","herb","herd","here","hero","herr","hers",
138   "hess","hibs","hick","hide","high","hike","hill","hilt","hind","hint",
139   "hips","hire","hiss","hits","hive","hiya","hmso","hoax","hogg","hold",
140   "hole","holt","holy","home","hong","hons","hood","hoof","hook","hoop",
141   "hope","hops","horn","hose","host","hour","hove","howe","howl","hrun",
142   "hues","huge","hugh","hugo","hulk","hull","hume","hump","hung","hunt",
143   "hurd","hurt","hush","huts","hyde","hype","iaea","iago","iain","ibid",
144   "iboa","iced","icon","idea","idle","idly","idol","igor","ills","inca",
145   "ince","inch","info","inns","insp","into","iona","ions","iowa","iran",
146   "iraq","iris","iron","isis","isle","itch","item","ivan","ives","ivor",
147   "jack","jade","jail","jake","jams","jane","jars","java","jaws","jazz",
148   "jean","jeep","jeff","jerk","jess","jest","jets","jett","jews","jill",
149   "jimi","joan","jobs","jock","joel","joey","john","join","joke","jolt",
150   "jose","josh","joys","juan","judd","jude","judi","judo","judy","jugs",
151   "july","jump","june","jung","junk","jury","just","kahn","kane","kant",
152   "karl","karr","kate","kath","katy","katz","kaye","keel","keen","keep",
153   "kemp","kent","kept","kerb","kerr","keys","khan","kick","kidd","kids",
154   "kiev","kiff","kill","kiln","kilo","kilt","kind","king","kirk","kiss",
155   "kite","kits","kiwi","knee","knew","knit","knob","knot","know","knox",
156   "koch","kohl","kong","kuhn","kurt","kyle","kyte","labs","lace","lack",
157   "lacy","lads","lady","laid","lain","lair","lais","lake","lama","lamb",
158   "lame","lamp","land","lane","lang","laos","laps","lard","lark","lass",
159   "last","late","lava","lawn","laws","lays","lazy","lead","leaf","leak",
160   "lean","leap","lear","leas","lech","lees","left","legs","lend","lens",
161   "lent","leon","less","lest","lets","levi","levy","leys","liam","liar",
162   "lice","lick","lids","lied","lien","lies","life","lift","like","lili",
163   "lily","lima","limb","lime","limp","lina","line","ling","link","lino",
164   "lion","lips","lira","lire","lisa","list","live","liza","load","loaf",
165   "loan","lobe","loch","lock","loco","loft","logo","logs","lois","lone",
166   "long","look","loom","loop","loos","loot","lord","lore","lori","lose",
167   "loss","lost","lots","loud","love","lowe","ltte","luce","luch","luck",
168   "lucy","ludo","luis","luke","lull","lump","lung","lure","lush","lust",
169   "lute","lyle","lyon","mabs","mace","mach","mack","made","maid","mail",
170   "main","mait","make","mala","male","mali","mall","malt","mama","mane",
171   "mann","mans","manx","many","maps","marc","mare","mark","marr","mars",
172   "marx","mary","mash","mask","mass","mast","mate","mats","matt","maud",
173   "mayo","maze","mead","meal","mean","meat","meek","meet","mega","melt",
174   "memo","mend","mens","menu","mere","mesh","mess","mice","mick","midi",
175   "mike","mild","mile","milk","mill","mime","mind","mine","minh","mini",
176   "mink","mins","mint","mips","mira","mire","miss","mist","mite","moan",
177   "moat","mobs","moby","mock","mode","modi","mold","mole","mona","monk",
178   "mono","mont","mood","moon","moor","moot","more","mori","moss","most",
179   "moth","mott","move","mrna","much","muck","mugs","muir","mule","mull",
180   "mums","muon","muse","must","mute","myra","nacl","naff","nail","name",
181   "nana","nape","nasa","nash","nato","nave","navy","neal","near","neat",
182   "neck","need","neil","nell","neon","nero","ness","nest","nets","news",
183   "next","nice","nick","niki","nile","nina","nine","niro","noah","node",
184   "nods","noel","noir","nome","nona","none","noon","nope","nora","norm",
185   "nose","note","noun","nova","nowt","nude","null","numb","nunn","nuns",
186   "nupe","nuts","oaks","oars","oath","oats","oban","obey","oboe","odds",
187   "oecd","offa","ohio","ohms","oils","oily","okay","olds","olga","oman",
188   "omar","omen","omit","once","ones","only","onto","onus","oops","opal",
189   "opcs","opec","open","oral","orcs","ores","orgy","oslo","otto","ould",
190   "ours","oust","outs","oval","oven","over","owed","owen","owes","owls",
191   "owns","oxen","pace","pack","pact","pads","page","pahl","paid","pain",
192   "pair","pale","pall","palm","pals","pane","pang","pans","papa","para",
193   "park","parr","part","pass","past","pate","path","paul","pave","pawn",
194   "paws","pays","peak","pear","peas","peat","peck","peel","peer","pegs",
195   "peng","penh","penn","pens","pepe","perm","pers","pert","peru","pest",
196   "pete","pets","pews","phew","phil","pick","pied","pier","pies","pigs",
197   "pike","pile","pill","pine","ping","pink","pins","pint","pipe","pips",
198   "pisa","piss","pits","pitt","pity","pius","plan","play","plea","plot",
199   "ploy","plug","plum","plus","pods","poem","poet","poke","pole","poll",
200   "polo","poly","pomp","pond","pons","pont","pony","pooh","pool","poor",
201   "pope","pops","pore","pork","porn","port","pose","posh","posi","post",
202   "pots","pour","pram","prat","pray","prep","pres","prey","prim","prix",
203   "prof","prop","pros","prow","pubs","puff","pugh","pull","pulp","pump",
204   "punk","punt","puny","pups","pure","push","puts","putt","quay","quid",
205   "quit","quiz","race","rack","racy","raft","rage","rags","raid","rail",
206   "rain","rake","ramp","rams","rang","rank","rape","rapt","rare","rash",
207   "rate","rats","rave","rays","rbge","rdbi","read","real","reap","rear",
208   "reds","reed","reef","reel","rees","refs","reid","rein","rely","rene",
209   "rent","reps","rest","retd","revd","revs","reza","rhee","riba","ribs",
210   "rica","rice","rich","rick","rico","ride","rife","rift","riga","rigs",
211   "rind","ring","rink","riot","ripe","risc","rise","risk","rita","rite",
212   "ritz","riva","rnli","road","roam","roar","robb","robe","rock","rode",
213   "rods","role","rolf","roll","roma","rome","roof","rook","room","root",
214   "rope","rory","rosa","rose","ross","rosy","rota","roth","rout","rowe",
215   "rows","rubs","ruby","ruck","rudd","rude","rugs","ruin","rule","rump",
216   "rune","rung","runs","ruse","rush","russ","rust","ruth","ryan","sack",
217   "safe","saga","sage","said","sail","sake","sale","salt","same","sand",
218   "sane","sang","sank","sans","sara","sash","saul","save","saws","says",
219   "sbus","scan","scar","scot","scsi","scum","seal","seam","sean","seas",
220   "seat","secs","sect","seed","seek","seem","seen","seep","sees","sega",
221   "sejm","self","sell","sema","semi","send","sent","sept","sera","serb",
222   "serc","seth","sets","seve","sewn","sexy","shae","shah","shai","sham",
223   "shaw","shed","shia","shih","shin","ship","shoe","shop","shot","show",
224   "shut","sick","side","sigh","sign","sikh","silk","sill","silt","sims",
225   "sine","sing","sink","sins","site","sits","size","skin","skip","skis",
226   "skye","slab","slag","slam","slap","slid","slim","slip","slit","slot",
227   "slow","slug","slum","slur","slut","smog","smug","snag","snap","snip",
228   "snob","snow","snub","snug","soak","soap","soar","sobs","sock","soda",
229   "sofa","soft","soho","soil","sold","sole","solo","some","song","sons",
230   "sony","soon","soot","sore","sort","soul","soup","sour","sown","sows",
231   "soya","span","spar","spat","spec","sped","spin","spit","spot","spun",
232   "spur","ssap","stab","stag","stan","star","stay","stem","step","stew",
233   "stir","stok","stop","stow","stub","stud","subs","such","suck","sued",
234   "suez","suit","sums","sung","sunk","suns","supt","sure","surf","suzi",
235   "suzy","swam","swan","swap","sway","swig","swim","tabs","tack","tact",
236   "taff","tags","tail","tait","take","tale","talk","tall","tame","tang",
237   "tank","tape","taps","tara","tart","task","tate","taut","taxi","teak",
238   "teal","team","tear","teas","tech","tecs","teen","tees","tell","tend",
239   "tens","tent","term","tess","test","text","thai","than","that","thaw",
240   "thee","them","then","theo","they","thin","this","thou","thud","thug",
241   "thus","tick","tide","tidy","tied","tier","ties","tile","till","tilt",
242   "time","tina","tins","tiny","tips","tire","tito","toad","toby","todd",
243   "toes","togo","toil","told","toll","tomb","tome","tone","toni","tons",
244   "tony","took","tool","tops","tore","torn","tort","tory","toss","tour",
245   "town","toys","tram","trap","tray","tree","trek","trim","trio","trip",
246   "trna","trod","trot","troy","true","tsar","tube","tubs","tuck","tuna",
247   "tune","tung","turf","turk","turn","tvei","twig","twin","twit","twos",
248   "tyne","type","tyre","ucta","uefa","ugly","uist","undo","unit","unix",
249   "unto","upon","urea","urge","urgh","used","user","uses","ussr","utah",
250   "vain","vale","vane","vans","vary","vase","vass","vast","vats","veal",
251   "veil","vein","vent","vera","verb","vern","very","vest","veto","vets",
252   "vial","vibe","vice","view","vile","vine","visa","vita","vivo","void",
253   "vole","volt","vote","vous","vows","wabi","wacc","wade","wage","wail",
254   "wait","wake","walk","wall","walt","wand","wang","want","ward","ware",
255   "warm","warn","warp","wars","wary","wash","wasp","watt","wave","wavy",
256   "ways","weak","wear","webb","webs","weed","week","weep","weir","well",
257   "went","wept","were","west","what","when","whig","whim","whip","whit",
258   "whoa","whom","wick","wide","wife","wigs","wild","will","wily","wind",
259   "wine","wing","wink","wins","wipe","wire","wiry","wise","wish","with",
260   "wits","woes","woke","wolf","womb","wont","wood","wool","word","wore",
261   "work","worm","worn","wove","wrap","wren","writ","wyre","yale","yang",
262   "yard","yarn","yawn","yeah","year","yell","yoga","yoke","yolk","york",
263   "your","yous","yuan","yuri","yves","zach","zack","zapt","zeal","zero",
264   "zest","zeta","zeus","zinc","zone","zoom","zoos","zzap"
265 };
266 
267 int debug = 0;
268 
269 
270 /* add the output and time of a shell command to message digest */
271 
gurgle(md_state * mdp,char * command)272 void gurgle(md_state *mdp, char *command)
273 {
274   FILE *f;
275   char buf[128];
276   long len = 0, l;
277   struct timeval t;
278 
279   f = popen(command, "r");
280   gettimeofday(&t, NULL);
281   md_add(mdp, &t, sizeof(t));
282   if (!f) {
283     fprintf(stderr, "External entropy source command '%s'\n"
284 	    "(one of several) failed.\n", command);
285     return;
286   }
287   while (!feof(f) && !ferror(f)) {
288     len += l = fread(buf, 1, sizeof(buf), f);
289     md_add(mdp, buf, l);
290   }
291   if (len == 0)
292     fprintf(stderr, "External entropy source command '%s'\n"
293 	    "returned no output.\n", command);
294   else
295     if (debug)
296       fprintf(stderr, "'%s' added %ld bytes.\n", command, len);
297   pclose(f);
298   gettimeofday(&t, NULL);
299   md_add(mdp, &t, sizeof(t));
300 }
301 
302 
303 /* A random bit generator. Hashes together various sources of entropy
304  * to provide a 16 byte high quality random seed */
305 
306 /* Determine the initial start state of the random bit generator */
307 
rbg_seed(unsigned char * r)308 void rbg_seed(unsigned char *r)
309 {
310   unsigned i;
311   md_state md;
312   struct {
313     clock_t clk;
314     pid_t pid;
315     uid_t uid;
316     pid_t ppid;
317   } entropy;
318 
319   md_init(&md);
320 
321   /* get entropy via some shell commands */
322   for (i = 0;  i < sizeof(entropy_env)/sizeof(char*); i++)
323     putenv(entropy_env[i]);
324   for (i = 0; i < sizeof(entropy_cmds)/sizeof(char*); i++)
325     gurgle(&md, entropy_cmds[i]);
326 
327   /* other minor sources of entropy */
328   entropy.clk = clock();
329   entropy.uid = getuid();
330   entropy.pid = getpid();
331   entropy.ppid = getppid();
332 
333   md_add(&md, &entropy, sizeof(entropy));
334 
335   md_close(&md, r);
336 }
337 
338 
339 /* Determine the next random bit generator state by hashing the
340  * previous state along with the the time and some magic string */
341 
rbg_iter(unsigned char * r)342 void rbg_iter(unsigned char *r)
343 {
344   md_state md;
345   struct timeval t;
346 
347   md_init(&md);
348   gettimeofday(&t, NULL);
349   md_add(&md, &t, sizeof(t));
350   md_add(&md, r, MD_LEN);
351   md_add(&md, "AutomaGic", 9);  /* feel free to change this as a site key */
352   md_close(&md, r);
353 }
354 
355 
356 /* Generate a random byte string s with len bytes from a
357  * seed string seed with length slen */
358 
random_string(const void * seed,size_t slen,void * s,size_t len)359 void random_string(const void *seed, size_t slen, void *s, size_t len)
360 {
361   md_state md;
362   unsigned char r[MD_LEN];
363   size_t i;
364   char j;
365 
366   assert(len <= 0xffffffff);
367   md_init(&md);
368   md_add(&md, seed, slen);
369   md_close(&md, r);
370   for (i = 0; i < len; i++) {
371     *((unsigned char *)s++) = r[0];
372     md_init(&md);
373     j = i >> 24;  md_add(&md, &j, 1);
374     j = i >> 16;  md_add(&md, &j, 1);
375     j = i >> 8;   md_add(&md, &j, 1);
376     j = i;        md_add(&md, &j, 1);
377     md_add(&md, r, MD_LEN);   /* this lines does not add entropy */
378     md_add(&md, seed, slen);  /* that is not already added here! */
379     md_close(&md, r);
380   }
381 }
382 
383 
384 /*
385  * Transform the first 6*chars bits of the binary string v into a chars
386  * character long string s. The encoding is a modification of the MIME
387  * base64 encoding where characters with easily confused glyphs are
388  * avoided (0 vs O, 1 vs. l vs. I).
389  */
390 
conv_base64(char * s,const unsigned char * v,int chars)391 void conv_base64(char *s, const unsigned char *v, int chars)
392 {
393   static const char tab[] =
394     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk%mnopqrstuvwxyz"
395     ":=23456789+/";
396   int i, j;
397 
398   for (i = 0; i < chars; i++) {
399     j = (i / 4) * 3;
400     switch (i % 4) {
401     case 0: *s++ = tab[  v[j]  >>2];                        break;
402     case 1: *s++ = tab[((v[j]  <<4) & 0x30) | (v[j+1]>>4)]; break;
403     case 2: *s++ = tab[((v[j+1]<<2) & 0x3c) | (v[j+2]>>6)]; break;
404     case 3: *s++ = tab[  v[j+2]     & 0x3f];                break;
405     }
406   }
407   *s++ = '\0';
408 }
409 
410 
411 /*
412  * Transform the first 5*chars bits of the binary string v into a chars
413  * character long string s. The encoding uses only lowercase letters
414  * and digit, in order to make it easy to communicate by voice (e.g.,
415  * using the NATO alphabet).
416  */
417 
conv_base32(char * s,const unsigned char * v,int chars)418 void conv_base32(char *s, const unsigned char *v, int chars)
419 {
420   static const char tab[] =
421     "abcdefghijkmnpqrstuvwxyz23456789";
422   int i, j = 0, k = 0;
423 
424   for (i = 0; i < chars; i++) {
425     if (k < 5) {
426       j = j << 8 | *(v++);
427       k += 8;
428     }
429     *s++ = tab[(j >> (k-5)) & 31];
430     k -= 5;
431   }
432   *s++ = '\0';
433 }
434 
435 
436 /*
437  * Normalize a password by removing whitespace etc. and converting
438  * l1| -> I, 0 -> O, \ -> /, just like otpw_verify() does.
439  */
pwnorm(char * password)440 void pwnorm(char *password) {
441   char *src, *dst;
442 
443   src = dst = password;
444   while (1) {
445     if (*src == 'l' || *src == '1' || *src == '|')
446       *dst++ = 'I';
447     else if (*src == '0')
448       *dst++ = 'O';
449     else if (*src == '\\')
450       *dst++ = '/';
451     else if ((*src >= 'A' && *src <= 'Z') ||
452 	     (*src >= 'a' && *src <= 'z') ||
453 	     (*src >= '2' && *src <= '9') ||
454 	     *src == ':' ||
455 	     *src == '%' ||
456 	     *src == '=' ||
457 	     *src == '+' ||
458 	     *src == '/')
459       *dst++ = *src;
460     else if (*src == '\0') {
461       *dst++ = *src;
462       return;
463     }
464     src++;
465   }
466 }
467 
468 
469 #define PW_BASE64  0
470 #define PW_WORD4   1
471 #define PW_BASE32  2
472 
473 /*
474  * Convert a random bit sequence into a printable password
475  *
476  * Input:      vr        random bit string
477  *             vlen      length of vr in bytes
478  *             type      0: modified base-64 encoding
479  *                       1: sequence of 4-letter words
480  *                       2: base-32 encoding (lowercase plus digits)
481  *             entropy   requested minimum entropy of password
482  *             buf       buffer for returning zero-terminated output password
483  *             buflen    length of buffer in bytes
484  *
485  * Returns negative value if provided combination of vlen, type,
486  * ent and buflen are not adequate, otherwise return length of
487  * generated password (excluding terminating '\0').
488  *
489  * If buf == NULL, return value depends on buflen:
490  *
491  *  0:  length of password that would have been generated
492  *  1:  number of its non-space password characters
493  *  2:  actually used entropy if buflen == 2
494  *  3:  maximum entropy that can be specified for given vlen
495  */
make_passwd(const void * vr,int vlen,int type,int entropy,char * buf,int buflen)496 int make_passwd(const void *vr, int vlen, int type, int entropy,
497 		char *buf, int buflen)
498 {
499   int pwchars;   /* number of characters in password */
500   int pwlen;     /* length of password, including whitespace */
501   int emax;
502   int i, j, k;
503   const unsigned char *v = vr;
504 
505   /* calculate length of output and actually used entropy */
506   switch (type) {
507   case PW_BASE32:
508     pwchars = (entropy + 4) / 5;
509     entropy = pwchars * 5;
510     pwlen = pwchars + (pwchars > 5 ? (pwchars - 1) / 4 : 0);
511     emax = ((vlen * 8) / 5) * 5;
512     break;
513   case PW_BASE64:
514     pwchars = (entropy + 5) / 6;
515     entropy = pwchars * 6;
516     pwlen = pwchars + (pwchars > 5 ? (pwchars - 1) / 4 : 0);
517     emax = ((vlen * 8) / 6) * 6;
518     break;
519   case PW_WORD4:
520     pwchars = 4 * ((entropy + 10) / 11);
521     entropy = 11 * ((entropy + 10) / 11);
522     pwlen = pwchars + pwchars / 4 - (pwchars > 0);
523     emax = ((vlen * 8) / 11) * 11;
524     break;
525   default:
526     return -1;
527   }
528 
529   if (!buf) {
530     switch (buflen) {
531     case 0: return pwlen;      /* including spaces */
532     case 1: return pwchars;    /* excluding spaces */
533     case 2: return entropy;
534     case 3: return emax;
535     default: return -2;
536     }
537   }
538   if (entropy > vlen * 8)
539     return -3;
540   if (pwlen >= buflen)
541     return -4;
542 
543   switch (type) {
544   case PW_BASE32:
545   case PW_BASE64:
546     if (type == PW_BASE32)
547       conv_base32(buf, v, pwchars);
548     else
549       conv_base64(buf, v, pwchars);
550     /* add spaces every 3-4 chars for readability (Bresenham's algorithm) */
551     i = pwchars - 1;
552     j = pwlen - 1;
553     k = (pwlen - pwchars) / 2;
554     while (i >= 0 && j >= 0) {
555       buf[j--] = buf[i--];
556       if ((k += pwlen - pwchars + 1) >= pwchars && j > 0) {
557 	buf[j--] = ' ';
558 	k -= pwchars;
559       }
560     }
561     buf[pwlen] = '\0';
562     break;
563   case PW_WORD4:
564     for (i = 0; i < pwchars/4; i++) {
565       k = 0;
566       for (j = i * 11; j < (i+1) * 11; j++)
567 	k = (k << 1) | ((v[j / 8] >> (j % 8)) & 1);
568       memcpy(buf + i * 5, word[k], 4);
569       buf[i * 5 + 4] = ' ';
570     }
571     buf[i * 5 - 1] = '\0';
572     break;
573   default:
574     return -1;
575   }
576 
577   assert((int) strlen(buf) == pwlen);
578 
579   return pwlen;
580 }
581 
582 
main(int argc,char ** argv)583 int main(int argc, char **argv)
584 {
585   unsigned char r[MD_LEN], h[MD_LEN];
586   md_state md;
587   int i, j, k, l;
588   struct otpw_pwdbuf *user = NULL, *pseudouser = NULL;
589   FILE *f;
590   char timestr[81], hostname[81], challenge[81];
591   char password1[1024], password2[1024];
592   char *password;
593   char *masterkey, *normal_masterkey = NULL;
594   int pwlen, pwchars, mklen;
595   char header[LINE_MAX];
596   char *fnout = NULL;
597   char *fntmp;
598   struct termios term, term_old;
599   int stdin_is_tty = 0;
600   int width = 79, height = 60, pages = 1, rows;
601   int header_lines = 4, random_order = 1;
602   int entropy = 48, emax, type = PW_BASE64;
603   int key_entropy = 76, key_type = PW_BASE32;
604   int use_masterkey = 0, regenerate = 0, unlock = 0;
605   int cols;
606   time_t t;
607   char *hbuf, *rndbuf;
608   int rndbuflen;
609   int challen = 3;    /* number of characters in challenge */
610   int hbuflen = challen + otpw_hlen + 1;
611   int help = 0;
612 
613   assert(md_selftest() == 0);
614   assert(otpw_hlen * 6 < MD_LEN * 8);
615   assert(otpw_hlen >= 8);
616 
617   /* read command line arguments */
618   for (i = 1; i < argc && !help; i++) {
619     if (argv[i][0] == '-')
620       for (j = 1; j > 0 && !help && argv[i][j] != 0; j++)
621         switch (argv[i][j]) {
622         case 'h':
623 	  if (++i >= argc) { help = 1; break; }
624 	  height = atoi(argv[i]);
625           j = -1;
626           break;
627         case 'w':
628 	  if (++i >= argc) { help = 1; break; };
629 	  width = atoi(argv[i]);
630           j = -1;
631           break;
632         case 's':
633 	  if (++i >= argc) { help = 1; break; }
634 	  pages = atoi(argv[i]);
635 	  if (pages < 1) {
636 	    fprintf(stderr, "Specify at least 1 page after -s!\n");
637 	    exit(1);
638 	  }
639           j = -1;
640           break;
641 	case 'e':
642 	  if (++i >= argc || (entropy = atoi(argv[i])) < 1)
643 	    { help = 1; break; }
644 	  j = -1;
645           break;
646 	case 'E':
647 	  if (++i >= argc || (key_entropy = atoi(argv[i])) < 1)
648 	    { help = 1; break; }
649           j = -1;
650           break;
651         case 'p':
652 	  if (strlen(argv[i]+j) == 2  &&
653 	      argv[i][j+1] >= '0' && argv[i][j+1] <= '1') {
654 	    /* just for backwards compatibility with version 1.3 */
655 	    type = argv[i][j+1] - '0';
656 	    j = -1;
657 	    break;
658 	  }
659 	  if (++i >= argc)
660 	    { help = 1; break; }
661 	  type = atoi(argv[i]);
662 	  if (make_passwd(NULL, 0, type, 0, NULL, 0) == -1)
663 	    { help = 1; break; }
664 	  j = -1;
665           break;
666         case 'P':
667 	  if (++i >= argc)
668 	    { help = 1; break; }
669 	  key_type = atoi(argv[i]);
670 	  if (make_passwd(NULL, 0, key_type, 0, NULL, 0) == -1)
671 	    { help = 1; break; }
672 	  j = -1;
673           break;
674         case 'f':
675 	  if (++i >= argc) { help = 1; break; }
676           fnout = argv[i];
677 	  j = -1;
678           break;
679 	case 'd':
680 	  debug = 1;
681 	  break;
682 	case 'n':
683 	  header_lines = 0;
684 	  break;
685 	case 'o':
686 	  random_order = 0;
687 	  break;
688 	case 'm':
689 	  use_masterkey = 1;
690 	  break;
691 	case 'k':
692 	  regenerate = 1;
693 	  break;
694 	case 'r':
695 	  rbg_seed(r);
696 	  rndbuflen = entropy / 8 + 16;
697 	  rndbuf = malloc(rndbuflen);
698 	  pwlen = make_passwd(rndbuf, rndbuflen,
699 			      type, entropy, NULL, 0); /* pwlen */
700 	  assert(pwlen >= 0);
701 	  password = malloc(pwlen+1);
702 	  if (!rndbuf || !password) {
703 	    fprintf(stderr, "Memory allocation error!\n");
704 	    exit(1);
705 	  }
706 	  random_string(r, MD_LEN, rndbuf, rndbuflen);
707 	  assert(make_passwd(rndbuf, rndbuflen, type, entropy, NULL, 3)
708 		 >= entropy); /* emax */
709 	  l = make_passwd(rndbuf, rndbuflen, type, entropy,
710 			  password, pwlen+1);
711 	  assert(l >= 0);
712 	  printf("%s\n", password);
713 	  rbg_iter(r); rbg_iter(r); /* memory scrubbing */
714 	  memset(password, 0xaa, pwlen); /* memory scrubbing */
715 	  exit(0);
716 	case 'l':
717 	  unlock = 1;
718 	  break;
719 	default:
720           help = 1;
721         }
722     else {
723       help = 1;
724     }
725   }
726 
727   if (fnout) {
728     /* if an output file was specified, drop privileges */
729     if (getuid() != geteuid()) {
730       if (setresuid(-1, getuid(), getuid()) || getuid() != geteuid()) {
731 	fprintf(stderr, "Dropping setuid privileges failed!\n");
732 	exit(1);
733       }
734     }
735     if (getgid() != getegid()) {
736       if (setresgid(-1, getgid(), getgid()) || getgid() != getegid()) {
737 	fprintf(stderr, "Dropping setgid privileges failed!\n");
738 	exit(1);
739       }
740     }
741   } else {
742     /* construct one-time password file path from relevant passwd entries */
743     otpw_getpwuid(getuid(), &user);
744     if (!user) {
745       fprintf(stderr, "Can't access your passwd database entry!\n");
746       exit(1);
747     }
748     if (getuid() != geteuid()) {
749       /* we are setuid pseudouser */
750       otpw_getpwuid(geteuid(), &pseudouser);
751       if (!pseudouser) {
752 	fprintf(stderr, "Can't access setuid pseudouser passwd entry!\n");
753 	exit(1);
754       }
755       fnout = (char *) malloc(strlen(pseudouser->pwd.pw_dir) + 1 +
756 			      strlen(user->pwd.pw_name) + 1);
757       if (!fnout) abort();
758       strcpy(fnout, pseudouser->pwd.pw_dir);
759       strcat(fnout, "/");
760       strcat(fnout, user->pwd.pw_name);
761     } else {
762       /* we are a normal user process */
763       fnout = (char *) malloc(strlen(user->pwd.pw_dir) + 1 +
764 			      strlen(otpw_file) + 1);
765       if (!fnout) abort();
766       strcpy(fnout, user->pwd.pw_dir);
767       strcat(fnout, "/");
768       strcat(fnout, otpw_file);
769     }
770   }
771 
772   if (help) {
773     /* Print brief usage instructions, then abort */
774     fprintf(stderr, "One-Time Password Generator v 1.5 -- Markus Kuhn\n\n");
775     fprintf(stderr, "%s [options]\n\n", argv[0]);
776     fprintf
777       (stderr, "Options: (default or current value in parenthesis)\n\n"
778        "  -h <int>\tnumber of output lines (60)\n"
779        "  -w <int>\tmax width of output lines (79)\n"
780        "  -s <int>\tnumber of output pages (1)\n"
781        "  -e <int>\tminimum entropy of each one-time password [bits]\n"
782        "\t\t(low security: <30, default: 48, high security: >60)\n"
783        "  -p 0\t\tpasswords from modified base64 encoding (default)\n"
784        "  -p 1\t\tpasswords from English 4-letter words\n"
785        "  -p 2\t\tpasswords use only lowercase letters and digits\n"
786        "  -f <filename>\toutput hash file (%s)\n",
787        fnout);
788     fprintf
789       (stderr,
790        "  -n\t\tdo not add header and footer lines to output\n"
791        "  -o\t\tuse passwords in printed order (default: random order)\n"
792        "  -m\t\tgenerate and display a master key for the password list\n"
793        "  -E <int>\tminimum entropy of master key [bits] (76)\n"
794        "  -P <int>\tencoding for master key (available values as for -p)\n"
795        "  -k\t\task for a master key and then regenerate a password\n\t\tlist"
796        " from it (this won't change %s)\n", fnout);
797     fprintf
798       (stderr,
799        "  -r\t\tsuggest a random password, then exit\n"
800        "  -l\t\tremove lock file %s%s, then exit\n",
801        fnout, otpw_locksuffix);
802     fprintf
803       (stderr,
804        "  -d\t\toutput debugging information\n"
805        "\nTypical uses:\n\n"
806        "  otpw-gen | lpr\n\n"
807        "    Generate password list and print it. The hash values for each "
808        "password\n    will be stored in %s for use during login "
809        "verification.\n\n"
810        "  otpw-gen -h 70 -s 2 | a2ps -1B -L 70 --borders no\n\n"
811        "    Generate password list and print it with nicer formatting.\n\n",
812        fnout);
813     exit(1);
814   }
815 
816   if (unlock) {
817     goto unlock;
818   }
819 
820   /* check and process some parameters */
821   rows = height - header_lines;
822   if (rows <= 0) {
823     fprintf(stderr, "At least %d lines per page required (incl. header)!\n",
824 	    header_lines + 1);
825     exit(1);
826   }
827   if (width < 64 && header_lines) {
828     fprintf(stderr, "Specify not less than 64 character "
829 	    "wide lines!\n");
830     exit(1);
831   }
832   if (use_masterkey && regenerate) {
833     fprintf(stderr, "Options -m and -k are mutually exclusive!\n");
834     exit(1);
835   }
836 
837   /* check whether entropy is ok */
838   if (entropy < emin) {
839     fprintf(stderr, "Entropy must be at least %d bits!\n", emin);
840     exit(1);
841   }
842   /* check whether entropy is ok */
843   if (key_entropy < 60) {
844     fprintf(stderr, "Masterkey entropy must be at least %d bits!\n", 60);
845     exit(1);
846   }
847 
848   /* allocate buffers for password generation */
849   rndbuflen = (entropy > key_entropy ? entropy : key_entropy) / 8 + 16;
850   rndbuf = malloc(rndbuflen);
851   pwlen   = make_passwd(NULL, rndbuflen, type, entropy, NULL, 0);
852   pwchars = make_passwd(NULL, rndbuflen, type, entropy, NULL, 1);
853   emax    = make_passwd(NULL, rndbuflen, type, entropy, NULL, 3);
854   assert(pwlen > 0 && pwchars > 0 && emax > entropy);
855   password = malloc(pwlen + 1);
856   if (!rndbuf || !password) {
857     fprintf(stderr, "Memory allocation error!\n");
858     exit(1);
859   }
860 
861   cols = (width + 2) / (challen + 1 + pwlen + 2);
862   if (cols < 1)
863     cols = 1;
864   if (pages * rows * cols > 1000) {
865     if (pages == 1)
866       rows = 1000 / cols;
867   }
868 
869   if (debug)
870     fprintf(stderr, "pwlen=%d, pwchars=%d, emax=%d, cols=%d, rows=%d\n",
871 	    pwlen, pwchars, emax, cols, rows);
872 
873   if (!regenerate) {
874     fprintf(stderr, "Generating random seed ...\n");
875     rbg_seed(r);
876 
877     fprintf(stderr,
878     "\nIf your paper password list is stolen, the thief should not gain\n"
879     "access to your account with this information alone. Therefore, you\n"
880     "need to memorize and enter below a prefix password. You will have to\n"
881     "enter that each time directly before entering the one-time password\n"
882     "(on the same line).\n\n"
883     "When you log in, a %d-digit password number will be displayed.  It\n"
884     "identifies the one-time password on your list that you have to append\n"
885     "to the prefix password. If another login to your account is in progress\n"
886     "at the same time, several password numbers may be shown and all\n"
887     "corresponding passwords have to be appended after the prefix\n"
888     "password. Best generate a new password list when you have used up half\n"
889     "of the old one.\n\n", challen);
890   }
891 
892   /* disable echo if stdin is a terminal */
893   if (!tcgetattr(fileno(stdin), &term)) {
894     stdin_is_tty = 1;
895     term_old = term;
896     term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
897     if (tcsetattr(fileno(stdin), TCSAFLUSH, &term)) {
898       perror("tcsetattr");
899       exit(1);
900     }
901   }
902   if (!regenerate) {
903     /* check whether there is an old password list, to warn against
904      * accidental overwriting */
905     f = fopen(fnout, "r");
906     if (f) {
907       fclose(f);
908       fprintf(stderr, "Overwrite existing password list '%s' (Y/n)? ",
909 	      fnout);
910       if (!fgets(password1, sizeof(password1), stdin) ||
911 	  (password1[0] != '\n' && password1[0] != 'y' && password1[0] != 'Y')) {
912 	if (stdin_is_tty)
913 	  tcsetattr(fileno(stdin), TCSANOW, &term_old);
914 	fprintf(stderr, "\nAborted.\n");
915 	exit(1);
916       }
917       fprintf(stderr, "\n\n");
918     }
919   }
920 
921   /* ask for prefix password */
922   if (regenerate) {
923     fprintf(stderr, "Enter master key: ");
924     fgets(password1, sizeof(password1), stdin);
925   } else {
926     fprintf(stderr, "Enter new prefix password: ");
927     fgets(password1, sizeof(password1), stdin);
928     fprintf(stderr, "\nReenter prefix password: ");
929     fgets(password2, sizeof(password2), stdin);
930   }
931   if (stdin_is_tty)
932     tcsetattr(fileno(stdin), TCSANOW, &term_old);
933   if (regenerate) {
934     if (*password1)
935       password1[strlen(password1)-1] = 0; /* remove last character = LF */
936     normal_masterkey = password1;
937     pwnorm(normal_masterkey);
938     md_init(&md);
939     md_add(&md, normal_masterkey, strlen(normal_masterkey));
940     md_close(&md, h);
941     if (h[0] & (0xff - (1 << (8 - MASTERKEY_CHECKBITS)) + 1)) {
942       fprintf(stderr, "\nIncorrect master key (invalid checkbits)!\n");
943       exit(1);
944     }
945   } else {
946     if (strcmp(password1, password2)) {
947       fprintf(stderr, "\nThe two entered passwords were not identical!\n");
948       exit(1);
949     }
950     if (*password1)
951       password1[strlen(password1)-1] = 0; /* remove last character = LF */
952   }
953 
954   if (regenerate)
955     fprintf(stderr, "\n\nRecreating one-time password list ...\n");
956   else
957     fprintf(stderr, "\n\nGenerating new one-time passwords ...\n\n");
958 
959   if (header_lines) {
960     /* prepare header line that uniquely identifies this password list */
961     time(&t);
962     strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M", localtime(&t));
963     strcpy(hostname, "???");
964     gethostname(hostname, sizeof(hostname));
965     hostname[sizeof(hostname)-1] = 0;
966     snprintf(header, sizeof(header), regenerate ?
967 	     "OTPW list regenerated %s on %s" NL NL :
968 	     "OTPW list generated %s on %s" NL NL,
969 	     timestr, hostname);
970   }
971 
972   /* allocate buffer for hash values */
973   hbuf = malloc(pages * rows * cols * hbuflen);
974   if (!hbuf) {
975     fprintf(stderr, "Memory allocation error!\n");
976     exit(1);
977   }
978 
979   assert(MASTERKEY_CHECKBITS < 8);
980   if (use_masterkey) {
981     mklen = make_passwd(0, 0, key_type, key_entropy + MASTERKEY_CHECKBITS,
982 			NULL, 0) + 1;
983     masterkey = malloc(mklen);
984     normal_masterkey = malloc(mklen);
985     if (!masterkey || !normal_masterkey) {
986       fprintf(stderr, "Memory allocation error!\n");
987       exit(1);
988     }
989     do {
990       /* generate new masterkey */
991       rbg_iter(r);
992       random_string(r, MD_LEN, rndbuf, rndbuflen);
993       make_passwd(rndbuf, rndbuflen, key_type,
994 		  key_entropy + MASTERKEY_CHECKBITS, masterkey, mklen);
995       strcpy(normal_masterkey, masterkey);
996       pwnorm(normal_masterkey);
997       md_init(&md);
998       md_add(&md, normal_masterkey, strlen(normal_masterkey));
999       md_close(&md, h);
1000       /* until its hash has the first MASTERKEY_CHECKBITS zero */
1001     } while (h[0] & (0xff - (1 << (8 - MASTERKEY_CHECKBITS)) + 1));
1002     fprintf(stderr, "Master key: %s\n"
1003 	    "(Option -k will recreate the same password list "
1004 	    "from this key.)\n\n", masterkey);
1005   }
1006 
1007   for (l = 0; l < pages; l++) {
1008     if (header_lines)
1009       fputs(header, stdout);
1010     for (i = 0; i < rows; i++) {
1011       for (j = 0; j < cols; j++) {
1012 	k = j * rows + i + l * rows * cols;
1013 	snprintf(challenge, sizeof(challenge), "%03d", k);
1014 	/* generate new password ... */
1015 	if (use_masterkey || regenerate) {
1016 	  /* ... from masterkey and challenge string */
1017 	  md_init(&md);
1018 	  md_add(&md, normal_masterkey, strlen(normal_masterkey));
1019 	  md_add(&md, challenge, strlen(challenge));
1020 	  md_close(&md, h);
1021 	  random_string(h, MD_LEN, rndbuf, rndbuflen);
1022 	} else {
1023 	  /* ... randomly */
1024 	  rbg_iter(r);
1025 	  random_string(r, MD_LEN, rndbuf, rndbuflen);
1026 	}
1027 	make_passwd(rndbuf, rndbuflen, type, entropy, password, pwlen + 1);
1028 	/* output challenge */
1029 	printf("%s %s", challenge, password);
1030 	if (j < cols - 1)
1031 	  printf("  ");
1032 	if (!regenerate) {
1033 	  /* hash password1 + pwnorm(password) and save result */
1034 	  md_init(&md);
1035 	  md_add(&md, password1, strlen(password1));
1036 	  pwnorm(password);
1037 	  md_add(&md, password, pwchars);
1038 	  md_close(&md, h);
1039 	  sprintf(hbuf + k * hbuflen, "%0*d", challen, k);
1040 	  conv_base64(hbuf + k*hbuflen + challen, h, otpw_hlen);
1041 	}
1042       }
1043       if (i < rows - 1)
1044 	printf(NL);
1045     }
1046     if (header_lines) {
1047       printf(NL NL "%*s", (cols*(challen + 1 + pwlen + 2) - 2)/2 + 50/2,
1048 	     "!!! REMEMBER: Enter the PREFIX PASSWORD first !!!");
1049     }
1050     if (l < pages - 1)
1051       printf(FF);
1052     else
1053       printf(NL);
1054   }
1055 
1056   /* paranoia RAM scrubbing (note that we can't scrub stdout/stdin portably) */
1057   rbg_iter(r);
1058   rbg_iter(r);
1059   md_init(&md);
1060   md_add(&md,
1061 	 "Always clean up all memory that was in contact with secrets!!!!!!",
1062 	 65);
1063   md_close(&md, h);
1064   memset(password1, 0xaa, sizeof(password1));
1065   memset(password2, 0xaa, sizeof(password2));
1066   memset(password, 0xaa, pwlen);
1067   fclose(stdout);
1068 
1069   if (regenerate)
1070     exit(0);
1071 
1072   /* create new hash file */
1073   fprintf(stderr, "Creating '%s' ...\n", fnout);
1074   fntmp = (char *) malloc(strlen(fnout)+strlen(tmpsuffix)+1);
1075   if (!fntmp) abort();
1076   strcpy(fntmp, fnout);
1077   strcat(fntmp, tmpsuffix);
1078   f = fopen(fntmp, "w");
1079   if (!f) {
1080     fprintf(stderr, "Can't write to '%s", fntmp);
1081     perror("'");
1082     exit(1);
1083   }
1084   if (fchmod(fileno(f), S_IRUSR | S_IWUSR)) {
1085     fprintf(stderr, "Can't fchmod '%s", fntmp);
1086     perror("'");
1087     exit(1);
1088   }
1089 
1090   /* write magic code for format identification */
1091   fprintf(f, "%s", otpw_magic);
1092   fprintf(f, "%d %d %d %d\n", pages * rows * cols, challen, otpw_hlen,
1093 	  pwchars);
1094 
1095   /* output all hash values in random permutation order */
1096   if (random_order) {
1097     for (k = pages * rows * cols - 1; k >= 0; k--) {
1098       rbg_iter(r);
1099       i = k > 0 ? (*(unsigned *) r) % k : 0;
1100       fprintf(f, "%s\n", hbuf + i*hbuflen);
1101       memcpy(hbuf + i*hbuflen, hbuf + k*hbuflen, hbuflen);
1102     }
1103   } else {
1104     for (k = 0; k < pages * rows * cols; k++)
1105       fprintf(f, "%s\n", hbuf + k*hbuflen);
1106   }
1107 
1108   fclose(f);
1109   if (rename(fntmp, fnout)) {
1110     fprintf(stderr, "Can't rename '%s' to '%s", fntmp, fnout);
1111     perror("'");
1112     exit(1);
1113   }
1114   free(fntmp);
1115 
1116   /* if we overwrite OTPW file, then any remaining lock is now meaningless */
1117  unlock:
1118   fntmp = (char *) malloc(strlen(fnout)+strlen(otpw_locksuffix)+1);
1119   if (!fntmp) abort();
1120   strcpy(fntmp, fnout);
1121   strcat(fntmp, otpw_locksuffix);
1122   if (unlink(fntmp)) {
1123     if (errno != ENOENT) {
1124       fprintf(stderr, "Can't delete lock file '%s", fntmp);
1125       perror("'");
1126       exit(1);
1127     }
1128   } else {
1129     fprintf(stderr, "Deleted lock file '%s'\n", fntmp);
1130   }
1131   free(fntmp);
1132 
1133   return 0;
1134 }
1135