xref: /freebsd/contrib/pam_modules/pam_passwdqc/passwdqc_random.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
1 /*
2  * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
3  */
4 
5 #include <stdio.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 
11 #include "passwdqc.h"
12 
13 #define SEPARATORS			"_,.;:-!&"
14 
15 static int read_loop(int fd, char *buffer, int count)
16 {
17 	int offset, block;
18 
19 	offset = 0;
20 	while (count > 0) {
21 		block = read(fd, &buffer[offset], count);
22 
23 		if (block < 0) {
24 			if (errno == EINTR) continue;
25 			return block;
26 		}
27 		if (!block) return offset;
28 
29 		offset += block;
30 		count -= block;
31 	}
32 
33 	return offset;
34 }
35 
36 char *_passwdqc_random(passwdqc_params_t *params)
37 {
38 	static char output[0x100];
39 	int bits;
40 	int use_separators, count, i;
41 	unsigned int length;
42 	char *start, *end;
43 	int fd;
44 	unsigned char bytes[2];
45 
46 	if (!(bits = params->random_bits))
47 		return NULL;
48 
49 	count = 1 + ((bits - 12) + 14) / 15;
50 	use_separators = ((bits + 11) / 12 != count);
51 
52 	length = count * 7 - 1;
53 	if (length >= sizeof(output) || (int)length > params->max)
54 		return NULL;
55 
56 	if ((fd = open("/dev/urandom", O_RDONLY)) < 0) return NULL;
57 
58 	length = 0;
59 	do {
60 		if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes)) {
61 			close(fd);
62 			return NULL;
63 		}
64 
65 		i = (((int)bytes[1] & 0x0f) << 8) | (int)bytes[0];
66 		start = _passwdqc_wordset_4k[i];
67 		end = memchr(start, '\0', 6);
68 		if (!end) end = start + 6;
69 		if (length + (end - start) >= sizeof(output) - 1) {
70 			close(fd);
71 			return NULL;
72 		}
73 		memcpy(&output[length], start, end - start);
74 		length += end - start;
75 		bits -= 12;
76 
77 		if (use_separators && bits > 3) {
78 			i = ((int)bytes[1] & 0x70) >> 4;
79 			output[length++] = SEPARATORS[i];
80 			bits -= 3;
81 		} else
82 		if (bits > 0)
83 			output[length++] = ' ';
84 	} while (bits > 0);
85 
86 	memset(bytes, 0, sizeof(bytes));
87 	output[length] = '\0';
88 
89 	close(fd);
90 
91 	return output;
92 }
93