154359Sroberto/*-
254359Sroberto * SPDX-License-Identifier: BSD-2-Clause
354359Sroberto *
4285612Sdelphij * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
554359Sroberto * All rights reserved.
654359Sroberto *
754359Sroberto * Redistribution and use in source and binary forms, with or without
8330141Sdelphij * modification, are permitted provided that the following conditions
9330141Sdelphij * are met:
1054359Sroberto * 1. Redistributions of source code must retain the above copyright
1182498Sroberto *    notice, this list of conditions and the following disclaimer.
1254359Sroberto * 2. Redistributions in binary form must reproduce the above copyright
13294569Sdelphij *    notice, this list of conditions and the following disclaimer in the
1454359Sroberto *    documentation and/or other materials provided with the distribution.
15285612Sdelphij *
16285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1954359Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2054359Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2254359Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2354359Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2454359Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2554359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2654359Sroberto * SUCH DAMAGE.
2754359Sroberto */
28285612Sdelphij
2954359Sroberto#include <sys/param.h>
3054359Sroberto#include <sys/errno.h>
3154359Sroberto#include <sys/limits.h>
3254359Sroberto#include <sys/proc.h>
3354359Sroberto#include <sys/random.h>
3454359Sroberto#include <sys/sysproto.h>
3554359Sroberto#include <sys/systm.h>
3654359Sroberto#include <sys/uio.h>
3754359Sroberto
3854359Sroberto#define GRND_VALIDFLAGS	(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE)
3954359Sroberto
40285612Sdelphij/*
4154359Sroberto * read_random_uio(9) returns EWOULDBLOCK if a nonblocking request would block,
4254359Sroberto * but the Linux API name is EAGAIN.  On FreeBSD, they have the same numeric
4354359Sroberto * value for now.
4454359Sroberto */
4554359SrobertoCTASSERT(EWOULDBLOCK == EAGAIN);
4654359Sroberto
4754359Srobertostatic int
48285612Sdelphijkern_getrandom(struct thread *td, void *user_buf, size_t buflen,
4954359Sroberto    unsigned int flags)
5054359Sroberto{
5154359Sroberto	struct uio auio;
5254359Sroberto	struct iovec aiov;
5354359Sroberto	int error;
5454359Sroberto
55285612Sdelphij	if ((flags & ~GRND_VALIDFLAGS) != 0)
5654359Sroberto		return (EINVAL);
5754359Sroberto	if (buflen > IOSIZE_MAX)
58285612Sdelphij		return (EINVAL);
5954359Sroberto
60285612Sdelphij	/*
6154359Sroberto	 * Linux compatibility: We have two choices for handling Linux's
6254359Sroberto	 * GRND_INSECURE.
6354359Sroberto	 *
6454359Sroberto	 * 1. We could ignore it completely (like GRND_RANDOM).  However, this
6554359Sroberto	 * might produce the surprising result of GRND_INSECURE requests
6654359Sroberto	 * blocking, when the Linux API does not block.
67289997Sglebius	 *
68289997Sglebius	 * 2. Alternatively, we could treat GRND_INSECURE requests as requests
69289997Sglebius	 * for GRND_NONBLOCK.  Here, the surprising result for Linux programs
70289997Sglebius	 * is that invocations with unseeded random(4) will produce EAGAIN,
71289997Sglebius	 * rather than garbage.
72289997Sglebius	 *
73289997Sglebius	 * Honoring the flag in the way Linux does seems fraught.  If we
74289997Sglebius	 * actually use the output of a random(4) implementation prior to
75289997Sglebius	 * seeding, we leak some entropy about the initial seed to attackers.
76289997Sglebius	 * This seems unacceptable -- it defeats the purpose of blocking on
77289997Sglebius	 * initial seeding.
78289997Sglebius	 *
79289997Sglebius	 * Secondary to that concern, before seeding we may have arbitrarily
80289997Sglebius	 * little entropy collected; producing output from zero or a handful of
81289997Sglebius	 * entropy bits does not seem particularly useful to userspace.
82293650Sglebius	 *
83293650Sglebius	 * If userspace can accept garbage, insecure non-random bytes, they can
84289997Sglebius	 * create their own insecure garbage with srandom(time(NULL)) or
85293650Sglebius	 * similar.  Asking the kernel to produce it from the secure
86289997Sglebius	 * getrandom(2) API seems inane.
87293650Sglebius	 *
88293650Sglebius	 * We elect to emulate GRND_INSECURE as an alternative spelling of
89293650Sglebius	 * GRND_NONBLOCK (2).
90294569Sdelphij	 */
91293650Sglebius	if ((flags & GRND_INSECURE) != 0)
92293650Sglebius		flags |= GRND_NONBLOCK;
93293650Sglebius
94293650Sglebius	if (buflen == 0) {
95293650Sglebius		td->td_retval[0] = 0;
96293650Sglebius		return (0);
97289997Sglebius	}
98289997Sglebius
99293650Sglebius	aiov.iov_base = user_buf;
100289997Sglebius	aiov.iov_len = buflen;
101289997Sglebius	auio.uio_iov = &aiov;
102289997Sglebius	auio.uio_iovcnt = 1;
103289997Sglebius	auio.uio_offset = 0;
104298699Sdelphij	auio.uio_resid = buflen;
105289997Sglebius	auio.uio_segflg = UIO_USERSPACE;
106289997Sglebius	auio.uio_rw = UIO_READ;
107289997Sglebius	auio.uio_td = td;
108289997Sglebius
109289997Sglebius	error = read_random_uio(&auio, (flags & GRND_NONBLOCK) != 0);
110289997Sglebius	if (error == 0)
111298699Sdelphij		td->td_retval[0] = buflen - auio.uio_resid;
112298699Sdelphij	return (error);
113298699Sdelphij}
114298699Sdelphij
115298699Sdelphij#ifndef _SYS_SYSPROTO_H_
116298699Sdelphijstruct getrandom_args {
117298699Sdelphij	void		*buf;
118298699Sdelphij	size_t		buflen;
119298699Sdelphij	unsigned int	flags;
120298699Sdelphij};
121298699Sdelphij#endif
122298699Sdelphij
123298699Sdelphijint
124298699Sdelphijsys_getrandom(struct thread *td, struct getrandom_args *uap)
125298699Sdelphij{
126298699Sdelphij	return (kern_getrandom(td, uap->buf, uap->buflen, uap->flags));
127298699Sdelphij}
128298699Sdelphij