xref: /freebsd/contrib/pam_modules/pam_passwdqc/passwdqc_random.c (revision 402783abd11a9f734cc08c6d9120347f316bdb3c)
10b0ecb56SDag-Erling Smørgrav /*
2402783abSDag-Erling Smørgrav  * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
30b0ecb56SDag-Erling Smørgrav  */
40b0ecb56SDag-Erling Smørgrav 
50b0ecb56SDag-Erling Smørgrav #include <stdio.h>
60b0ecb56SDag-Erling Smørgrav #include <string.h>
70b0ecb56SDag-Erling Smørgrav #include <unistd.h>
80b0ecb56SDag-Erling Smørgrav #include <errno.h>
90b0ecb56SDag-Erling Smørgrav #include <fcntl.h>
100b0ecb56SDag-Erling Smørgrav 
110b0ecb56SDag-Erling Smørgrav #include "passwdqc.h"
120b0ecb56SDag-Erling Smørgrav 
130b0ecb56SDag-Erling Smørgrav #define SEPARATORS			"_,.;:-!&"
140b0ecb56SDag-Erling Smørgrav 
read_loop(int fd,char * buffer,int count)150b0ecb56SDag-Erling Smørgrav static int read_loop(int fd, char *buffer, int count)
160b0ecb56SDag-Erling Smørgrav {
170b0ecb56SDag-Erling Smørgrav 	int offset, block;
180b0ecb56SDag-Erling Smørgrav 
190b0ecb56SDag-Erling Smørgrav 	offset = 0;
200b0ecb56SDag-Erling Smørgrav 	while (count > 0) {
210b0ecb56SDag-Erling Smørgrav 		block = read(fd, &buffer[offset], count);
220b0ecb56SDag-Erling Smørgrav 
230b0ecb56SDag-Erling Smørgrav 		if (block < 0) {
240b0ecb56SDag-Erling Smørgrav 			if (errno == EINTR) continue;
250b0ecb56SDag-Erling Smørgrav 			return block;
260b0ecb56SDag-Erling Smørgrav 		}
270b0ecb56SDag-Erling Smørgrav 		if (!block) return offset;
280b0ecb56SDag-Erling Smørgrav 
290b0ecb56SDag-Erling Smørgrav 		offset += block;
300b0ecb56SDag-Erling Smørgrav 		count -= block;
310b0ecb56SDag-Erling Smørgrav 	}
320b0ecb56SDag-Erling Smørgrav 
330b0ecb56SDag-Erling Smørgrav 	return offset;
340b0ecb56SDag-Erling Smørgrav }
350b0ecb56SDag-Erling Smørgrav 
_passwdqc_random(passwdqc_params_t * params)360b0ecb56SDag-Erling Smørgrav char *_passwdqc_random(passwdqc_params_t *params)
370b0ecb56SDag-Erling Smørgrav {
380b0ecb56SDag-Erling Smørgrav 	static char output[0x100];
390b0ecb56SDag-Erling Smørgrav 	int bits;
40402783abSDag-Erling Smørgrav 	int use_separators, count, i;
41402783abSDag-Erling Smørgrav 	unsigned int length;
420b0ecb56SDag-Erling Smørgrav 	char *start, *end;
430b0ecb56SDag-Erling Smørgrav 	int fd;
440b0ecb56SDag-Erling Smørgrav 	unsigned char bytes[2];
450b0ecb56SDag-Erling Smørgrav 
460b0ecb56SDag-Erling Smørgrav 	if (!(bits = params->random_bits))
470b0ecb56SDag-Erling Smørgrav 		return NULL;
480b0ecb56SDag-Erling Smørgrav 
490b0ecb56SDag-Erling Smørgrav 	count = 1 + ((bits - 12) + 14) / 15;
500b0ecb56SDag-Erling Smørgrav 	use_separators = ((bits + 11) / 12 != count);
510b0ecb56SDag-Erling Smørgrav 
520b0ecb56SDag-Erling Smørgrav 	length = count * 7 - 1;
53402783abSDag-Erling Smørgrav 	if (length >= sizeof(output) || (int)length > params->max)
54402783abSDag-Erling Smørgrav 		return NULL;
550b0ecb56SDag-Erling Smørgrav 
560b0ecb56SDag-Erling Smørgrav 	if ((fd = open("/dev/urandom", O_RDONLY)) < 0) return NULL;
570b0ecb56SDag-Erling Smørgrav 
580b0ecb56SDag-Erling Smørgrav 	length = 0;
590b0ecb56SDag-Erling Smørgrav 	do {
600b0ecb56SDag-Erling Smørgrav 		if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes)) {
610b0ecb56SDag-Erling Smørgrav 			close(fd);
620b0ecb56SDag-Erling Smørgrav 			return NULL;
630b0ecb56SDag-Erling Smørgrav 		}
640b0ecb56SDag-Erling Smørgrav 
65402783abSDag-Erling Smørgrav 		i = (((int)bytes[1] & 0x0f) << 8) | (int)bytes[0];
66402783abSDag-Erling Smørgrav 		start = _passwdqc_wordset_4k[i];
670b0ecb56SDag-Erling Smørgrav 		end = memchr(start, '\0', 6);
680b0ecb56SDag-Erling Smørgrav 		if (!end) end = start + 6;
690b0ecb56SDag-Erling Smørgrav 		if (length + (end - start) >= sizeof(output) - 1) {
700b0ecb56SDag-Erling Smørgrav 			close(fd);
710b0ecb56SDag-Erling Smørgrav 			return NULL;
720b0ecb56SDag-Erling Smørgrav 		}
730b0ecb56SDag-Erling Smørgrav 		memcpy(&output[length], start, end - start);
740b0ecb56SDag-Erling Smørgrav 		length += end - start;
750b0ecb56SDag-Erling Smørgrav 		bits -= 12;
760b0ecb56SDag-Erling Smørgrav 
770b0ecb56SDag-Erling Smørgrav 		if (use_separators && bits > 3) {
78402783abSDag-Erling Smørgrav 			i = ((int)bytes[1] & 0x70) >> 4;
79402783abSDag-Erling Smørgrav 			output[length++] = SEPARATORS[i];
800b0ecb56SDag-Erling Smørgrav 			bits -= 3;
810b0ecb56SDag-Erling Smørgrav 		} else
820b0ecb56SDag-Erling Smørgrav 		if (bits > 0)
830b0ecb56SDag-Erling Smørgrav 			output[length++] = ' ';
840b0ecb56SDag-Erling Smørgrav 	} while (bits > 0);
850b0ecb56SDag-Erling Smørgrav 
860b0ecb56SDag-Erling Smørgrav 	memset(bytes, 0, sizeof(bytes));
870b0ecb56SDag-Erling Smørgrav 	output[length] = '\0';
880b0ecb56SDag-Erling Smørgrav 
890b0ecb56SDag-Erling Smørgrav 	close(fd);
900b0ecb56SDag-Erling Smørgrav 
910b0ecb56SDag-Erling Smørgrav 	return output;
920b0ecb56SDag-Erling Smørgrav }
93