193787Sdes/*
294691Sdes * Copyright (c) 2000-2002 by Solar Designer. See LICENSE.
393787Sdes */
493787Sdes
593787Sdes#include <stdio.h>
693787Sdes#include <string.h>
793787Sdes#include <unistd.h>
893787Sdes#include <errno.h>
993787Sdes#include <fcntl.h>
1093787Sdes
1193787Sdes#include "passwdqc.h"
1293787Sdes
1393787Sdes#define SEPARATORS			"_,.;:-!&"
1493787Sdes
1593787Sdesstatic int read_loop(int fd, char *buffer, int count)
1693787Sdes{
1793787Sdes	int offset, block;
1893787Sdes
1993787Sdes	offset = 0;
2093787Sdes	while (count > 0) {
2193787Sdes		block = read(fd, &buffer[offset], count);
2293787Sdes
2393787Sdes		if (block < 0) {
2493787Sdes			if (errno == EINTR) continue;
2593787Sdes			return block;
2693787Sdes		}
2793787Sdes		if (!block) return offset;
2893787Sdes
2993787Sdes		offset += block;
3093787Sdes		count -= block;
3193787Sdes	}
3293787Sdes
3393787Sdes	return offset;
3493787Sdes}
3593787Sdes
3693787Sdeschar *_passwdqc_random(passwdqc_params_t *params)
3793787Sdes{
3893787Sdes	static char output[0x100];
3993787Sdes	int bits;
4094691Sdes	int use_separators, count, i;
4194691Sdes	unsigned int length;
4293787Sdes	char *start, *end;
4393787Sdes	int fd;
4493787Sdes	unsigned char bytes[2];
4593787Sdes
4693787Sdes	if (!(bits = params->random_bits))
4793787Sdes		return NULL;
4893787Sdes
4993787Sdes	count = 1 + ((bits - 12) + 14) / 15;
5093787Sdes	use_separators = ((bits + 11) / 12 != count);
5193787Sdes
5293787Sdes	length = count * 7 - 1;
5394691Sdes	if (length >= sizeof(output) || (int)length > params->max)
5494691Sdes		return NULL;
5593787Sdes
5693787Sdes	if ((fd = open("/dev/urandom", O_RDONLY)) < 0) return NULL;
5793787Sdes
5893787Sdes	length = 0;
5993787Sdes	do {
6093787Sdes		if (read_loop(fd, bytes, sizeof(bytes)) != sizeof(bytes)) {
6193787Sdes			close(fd);
6293787Sdes			return NULL;
6393787Sdes		}
6493787Sdes
6594691Sdes		i = (((int)bytes[1] & 0x0f) << 8) | (int)bytes[0];
6694691Sdes		start = _passwdqc_wordset_4k[i];
6793787Sdes		end = memchr(start, '\0', 6);
6893787Sdes		if (!end) end = start + 6;
6993787Sdes		if (length + (end - start) >= sizeof(output) - 1) {
7093787Sdes			close(fd);
7193787Sdes			return NULL;
7293787Sdes		}
7393787Sdes		memcpy(&output[length], start, end - start);
7493787Sdes		length += end - start;
7593787Sdes		bits -= 12;
7693787Sdes
7793787Sdes		if (use_separators && bits > 3) {
7894691Sdes			i = ((int)bytes[1] & 0x70) >> 4;
7994691Sdes			output[length++] = SEPARATORS[i];
8093787Sdes			bits -= 3;
8193787Sdes		} else
8293787Sdes		if (bits > 0)
8393787Sdes			output[length++] = ' ';
8493787Sdes	} while (bits > 0);
8593787Sdes
8693787Sdes	memset(bytes, 0, sizeof(bytes));
8793787Sdes	output[length] = '\0';
8893787Sdes
8993787Sdes	close(fd);
9093787Sdes
9193787Sdes	return output;
9293787Sdes}
93