Skip to content

Instantly share code, notes, and snippets.

@nonakap
Created July 17, 2015 14:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nonakap/243f0e87050a53b1cbf6 to your computer and use it in GitHub Desktop.
Save nonakap/243f0e87050a53b1cbf6 to your computer and use it in GitHub Desktop.
doas(1): porting OpenBSD doas(1) command to NetBSD.
diff --git a/usr.bin/doas/Makefile b/usr.bin/doas/Makefile
index 809fe0b..6610edb 100644
--- a/usr.bin/doas/Makefile
+++ b/usr.bin/doas/Makefile
@@ -1,14 +1,31 @@
+# $NetBSD$
# $OpenBSD: Makefile,v 1.1 2015/07/16 20:44:21 tedu Exp $
-SRCS= parse.y doas.c
+.include <bsd.own.mk>
+
+USE_FORT?=yes
+WARN= 4
PROG= doas
+SRCS= parse.y doas.c
MAN= doas.1 doas.conf.5
-BINOWN= root
+BINOWN= root
BINMODE=4555
-CFLAGS+= -I${.CURDIR}
-COPTS+= -Wall
+CPPFLAGS+=-I${.CURDIR}
+CPPFLAGS+=-DHAVE_LOGIN_CAP_H
+CPPFLAGS+=-DHAVE_INTTYPES_H
+CPPFLAGS+=-DHAVE_REALLOCARR
+CPPFLAGS+=-DHAVE_STRTOI
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if ${MKPAM} != "no"
+CPPFLAGS+= -DUSE_PAM
+LDADD+= -lpam ${PAM_STATIC_LDADD}
+DPADD+= ${LIBPAM} ${PAM_STATIC_DPADD}
+.endif
.include <bsd.prog.mk>
diff --git a/usr.bin/doas/doas.1 b/usr.bin/doas/doas.1
index b9bf0d3..2e33b54 100644
--- a/usr.bin/doas/doas.1
+++ b/usr.bin/doas/doas.1
@@ -1,3 +1,4 @@
+.\" $NetBSD$
.\" $OpenBSD: doas.1,v 1.1 2015/07/16 20:44:21 tedu Exp $
.\"
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
diff --git a/usr.bin/doas/doas.c b/usr.bin/doas/doas.c
index 3570cca..7ef2d64 100644
--- a/usr.bin/doas/doas.c
+++ b/usr.bin/doas/doas.c
@@ -1,3 +1,4 @@
+/* $NetBSD$ */
/* $OpenBSD: doas.c,v 1.6 2015/07/16 23:22:08 nicm Exp $ */
/*
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
@@ -18,9 +19,10 @@
#include <sys/types.h>
#include <sys/stat.h>
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
#include <limits.h>
-#include <login_cap.h>
-#include <bsd_auth.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -30,6 +32,21 @@
#include <grp.h>
#include <syslog.h>
+#if defined(HAVE_LOGIN_CAP_H)
+#include <login_cap.h>
+#endif
+
+#if defined(USE_BSD_AUTH)
+#include <bsd_auth.h>
+#endif
+
+#if defined(USE_PAM)
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+
+static struct pam_conv pamc = { openpam_ttyconv, NULL };
+#endif
+
#include "doas.h"
static void __dead
@@ -40,7 +57,7 @@ usage(void)
}
size_t
-arraylen(const char **arr)
+arraylen(const char * const *arr)
{
size_t cnt = 0;
while (*arr) {
@@ -54,15 +71,25 @@ static int
parseuid(const char *s, uid_t *uid)
{
struct passwd *pw;
+#if defined(HAVE_STRTONUM)
const char *errstr;
+#else
+ int error;
+#endif
if ((pw = getpwnam(s)) != NULL) {
*uid = pw->pw_uid;
return 0;
}
+#if defined(HAVE_STRTONUM)
*uid = strtonum(s, 0, UID_MAX, &errstr);
if (errstr)
return -1;
+#else
+ *uid = strtoi(s, NULL, 10, 0, UID_MAX, &error);
+ if (error)
+ return -1;
+#endif
return 0;
}
@@ -82,14 +109,24 @@ static gid_t
strtogid(const char *s)
{
struct group *gr;
- const char *errstr;
gid_t gid;
+#if defined(HAVE_STRTONUM)
+ const char *errstr;
+#else
+ int error;
+#endif
if ((gr = getgrnam(s)) != NULL)
return gr->gr_gid;
+#if defined(HAVE_STRTONUM)
gid = strtonum(s, 0, GID_MAX, &errstr);
if (errstr)
return -1;
+#else
+ gid = strtoi(s, NULL, 10, 0, GID_MAX, &error);
+ if (error)
+ return -1;
+#endif
return gid;
}
@@ -101,7 +138,7 @@ match(uid_t uid, gid_t *groups, int ngroups, uid_t target, const char *cmd,
if (r->ident[0] == ':') {
gid_t rgid = strtogid(r->ident + 1);
- if (rgid == -1)
+ if (rgid == (gid_t)-1)
return 0;
for (i = 0; i < ngroups; i++) {
if (rgid == groups[i])
@@ -161,12 +198,12 @@ parseconfig(const char *filename)
}
static int
-copyenvhelper(const char **oldenvp, const char **safeset, int nsafe,
+copyenvhelper(const char * const *oldenvp, const char **safeset, int nsafe,
char **envp, int ei)
{
int i;
for (i = 0; i < nsafe; i++) {
- const char **oe = oldenvp;
+ const char * const *oe = oldenvp;
while (*oe) {
size_t len = strlen(safeset[i]);
if (strncmp(*oe, safeset[i], len) == 0 &&
@@ -182,9 +219,9 @@ copyenvhelper(const char **oldenvp, const char **safeset, int nsafe,
}
static char **
-copyenv(const char **oldenvp, struct rule *rule)
+copyenv(const char * const *oldenvp, struct rule *rule)
{
- const char *safeset[] = {
+ static const char *safeset[] = {
"DISPLAY", "HOME", "LOGNAME", "MAIL", "SHELL",
"PATH", "TERM", "USER", "USERNAME",
NULL,
@@ -195,12 +232,18 @@ copyenv(const char **oldenvp, struct rule *rule)
const char **extra;
int ei;
int i, j;
-
+
if ((rule->options & KEEPENV) && !rule->envlist) {
j = arraylen(oldenvp);
+#if defined(HAVE_REALLOCARRAY)
envp = reallocarray(NULL, j + 1, sizeof(char *));
if (!envp)
err(1, "reallocarray");
+#else
+ envp = NULL;
+ if (reallocarr(&envp, j + 1, sizeof(char *)))
+ err(1, "reallocarr");
+#endif
for (i = 0; i < j; i++) {
if (!(envp[i] = strdup(oldenvp[i])))
err(1, "strdup");
@@ -222,9 +265,15 @@ copyenv(const char **oldenvp, struct rule *rule)
}
}
+#if defined(HAVE_REALLOCARRAY)
envp = reallocarray(NULL, nsafe + nextras + 1, sizeof(char *));
if (!envp)
err(1, "can't allocate new environment");
+#else
+ envp = NULL;
+ if (reallocarr(&envp, nsafe + nextras + 1, sizeof(char *)))
+ err(1, "can't allocate new environment");
+#endif
ei = 0;
ei = copyenvhelper(oldenvp, safeset, nsafe, envp, ei);
@@ -244,6 +293,8 @@ fail(void)
int
main(int argc, char **argv, char **envp)
{
+ static const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
+ "/usr/local/bin:/usr/local/sbin";
char cmdline[1024];
char myname[32];
uid_t uid, target = 0;
@@ -253,8 +304,11 @@ main(int argc, char **argv, char **envp)
struct rule *rule;
const char *cmd;
int i, ch;
- const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
- "/usr/local/bin:/usr/local/sbin";
+#if defined(USE_PAM)
+ pam_handle_t *pamh = NULL;
+ int pam_err;
+ int pam_silent = PAM_SILENT;
+#endif
parseconfig("/etc/doas.conf");
@@ -299,22 +353,84 @@ main(int argc, char **argv, char **envp)
}
if (!(rule->options & NOPASS)) {
+#if defined(USE_BSD_AUTH)
if (!auth_userokay(myname, NULL, NULL, NULL)) {
syslog(LOG_AUTHPRIV | LOG_NOTICE,
"failed password for %s", myname);
fail();
}
+#elif defined(USE_PAM)
+#define PAM_END(msg) do { \
+ syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err)); \
+ warnx("%s: %s", msg, pam_strerror(pamh, pam_err)); \
+ pam_end(pamh, pam_err); \
+ exit(EXIT_FAILURE); \
+} while (/*CONSTCOND*/0)
+
+ pam_err = pam_start("doas", myname, &pamc, &pamh);
+ if (pam_err != PAM_SUCCESS) {
+ if (pamh != NULL)
+ PAM_END("pam_start");
+ syslog(LOG_ERR, "pam_start failed: %s",
+ pam_strerror(pamh, pam_err));
+ errx(EXIT_FAILURE, "pam_start failed");
+ }
+
+ switch (pam_err = pam_authenticate(pamh, pam_silent)) {
+ case PAM_SUCCESS:
+ switch (pam_err = pam_acct_mgmt(pamh, pam_silent)) {
+ case PAM_SUCCESS:
+ break;
+
+ case PAM_NEW_AUTHTOK_REQD:
+ pam_err = pam_chauthtok(pamh,
+ pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
+ if (pam_err != PAM_SUCCESS)
+ PAM_END("pam_chauthtok");
+ break;
+
+ case PAM_AUTH_ERR:
+ case PAM_USER_UNKNOWN:
+ case PAM_MAXTRIES:
+ syslog(LOG_AUTHPRIV | LOG_NOTICE,
+ "failed auth for %s", myname);
+ fail();
+ break;
+
+ default:
+ PAM_END("pam_acct_mgmt");
+ break;
+ }
+ break;
+
+ case PAM_AUTH_ERR:
+ case PAM_USER_UNKNOWN:
+ case PAM_MAXTRIES:
+ syslog(LOG_AUTHPRIV | LOG_NOTICE,
+ "failed auth for %s", myname);
+ fail();
+ break;
+
+ default:
+ PAM_END("pam_authenticate");
+ break;
+ }
+ pam_end(pamh, pam_err);
+#else
+#error No auth module!
+#endif
}
- envp = copyenv((const char **)envp, rule);
+ envp = copyenv((const char * const *)envp, rule);
pw = getpwuid(target);
if (!pw)
errx(1, "no passwd entry for target");
+#if defined(HAVE_LOGIN_CAP_H)
if (setusercontext(NULL, pw, target, LOGIN_SETGROUP |
LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK |
LOGIN_SETUSER) != 0)
errx(1, "failed to set user context for target");
-
+#endif
syslog(LOG_AUTHPRIV | LOG_INFO, "%s ran command as %s: %s",
myname, pw->pw_name, cmdline);
setenv("PATH", safepath, 1);
diff --git a/usr.bin/doas/doas.conf.5 b/usr.bin/doas/doas.conf.5
index ddfdfd6..4fd85e2 100644
--- a/usr.bin/doas/doas.conf.5
+++ b/usr.bin/doas/doas.conf.5
@@ -1,3 +1,4 @@
+.\" $NetBSD$
.\" $OpenBSD: doas.conf.5,v 1.2 2015/07/16 21:24:07 nicm Exp $
.\"
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
diff --git a/usr.bin/doas/doas.h b/usr.bin/doas/doas.h
index b6d0275..4af3fd2 100644
--- a/usr.bin/doas/doas.h
+++ b/usr.bin/doas/doas.h
@@ -1,3 +1,4 @@
+/* $NetBSD$ */
struct rule {
int action;
@@ -11,10 +12,30 @@ struct rule {
extern struct rule **rules;
extern int nrules, maxrules;
-size_t arraylen(const char **);
+size_t arraylen(const char * const *);
#define PERMIT 1
#define DENY 2
#define NOPASS 0x1
#define KEEPENV 0x2
+
+#if !defined(HAVE_REALLOCARRAY) && !defined(HAVE_REALLOCARR)
+int reallocarr(void *ptr, size_t num, size_t size);
+#endif /* !HAVE_REALLOCARRAY && !HAVE_REALLOCARR */
+
+#if !defined(HAVE_STRTOI) && !defined(HAVE_STRTONUM)
+#if defined(HAVE_INTTYPES_H)
+#include <stdint.h>
+#elif defined(HAVE_LONG_LONG_INT)
+typedef long long intmax_t;
+#else /* !HAVE_INTTYPES_H && !HAVE_LONG_LONG_INT */
+typedef long intmax_t;
+#endif /* HAVE_LONG_LONG_INT */
+intmax_t strtoi(const char *nptr, char **endptr, int base, intmax_t lo,
+ intmax_t hi, int *rstatus);
+#endif /* !HAVE_STRTOI && !HAVE_STRTONUM */
+
+#if !defined(HAVE_EXECVPE)
+int execvpe(const char *file, char * const *argv, char * const *envp);
+#endif /* !HAVE_EXECVPE */
diff --git a/usr.bin/doas/execvpe.c b/usr.bin/doas/execvpe.c
new file mode 100644
index 0000000..4a74d4b
--- /dev/null
+++ b/usr.bin/doas/execvpe.c
@@ -0,0 +1,48 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (C) 2015 NONAKA Kimihiro <nonakap@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+#include "doas.h"
+
+#if !defined(HAVE_EXECVPE)
+
+extern char **environ;
+
+int
+execvpe(const char *file, char * const *argv, char * const *envp)
+{
+ char **oldenvp = environ;
+ int error;
+
+ environ = (char **)(long)envp; /* XXX */
+ error = execvp(file, argv);
+ environ = oldenvp;
+ return error;
+}
+
+#endif /* !HAVE_EXECVPE */
diff --git a/usr.bin/doas/parse.y b/usr.bin/doas/parse.y
index 089dc4f..9b084c2 100644
--- a/usr.bin/doas/parse.y
+++ b/usr.bin/doas/parse.y
@@ -1,3 +1,4 @@
+/* $NetBSD$ */
/* $OpenBSD: parse.y,v 1.4 2015/07/16 23:02:56 nicm Exp $ */
/*
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
@@ -23,6 +24,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <err.h>
#include "doas.h"
@@ -77,8 +79,13 @@ rule: action ident target cmd {
maxrules = 63;
else
maxrules *= 2;
+#if defined(HAVE_REALLOCARRAY)
if (!(rules = reallocarray(rules, maxrules, sizeof(*rules))))
errx(1, "can't allocate rules");
+#else
+ if (reallocarr(&rules, maxrules, sizeof(*rules)))
+ errx(1, "can't allocate rules");
+#endif
}
rules[nrules++] = r;
} ;
@@ -116,8 +123,14 @@ envlist: /* empty */ {
errx(1, "can't allocate envlist");
} | envlist TSTRING {
int nenv = arraylen($1.envlist);
+#if defined(HAVE_REALLOCARRAY)
if (!($$.envlist = reallocarray($1.envlist, nenv + 2, sizeof(char *))))
errx(1, "can't allocate envlist");
+#else
+ $$.envlist = $1.envlist;
+ if (reallocarr(&$$.envlist, nenv + 2, sizeof(char *)))
+ errx(1, "can't allocate envlist");
+#endif
$$.envlist[nenv] = $2.str;
$$.envlist[nenv + 1] = NULL;
}
@@ -150,7 +163,7 @@ yyerror(const char *fmt, ...)
verrx(1, fmt, va);
}
-struct keyword {
+static const struct keyword {
const char *word;
int token;
} keywords[] = {
@@ -206,7 +219,7 @@ eow:
*p = 0;
if (c != EOF)
ungetc(c, yyfp);
- for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
+ for (i = 0; i < (int)(sizeof(keywords) / sizeof(keywords[0])); i++) {
if (strcmp(buf, keywords[i].word) == 0)
return keywords[i].token;
}
diff --git a/usr.bin/doas/reallocarr.c b/usr.bin/doas/reallocarr.c
new file mode 100644
index 0000000..342bba2
--- /dev/null
+++ b/usr.bin/doas/reallocarr.c
@@ -0,0 +1,73 @@
+/* $NetBSD$ */
+/* NetBSD: reallocarr.c,v 1.2 2015/07/16 00:03:59 kamil Exp */
+
+/*-
+ * Copyright (c) 2015 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+/* Old POSIX has SIZE_MAX in limits.h */
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doas.h"
+
+#if !defined(HAVE_REALLOCARR) && !defined(HAVE_REALLOCARRAY)
+
+int
+reallocarr(void *ptr, size_t num, size_t size)
+{
+ int saved_errno, result;
+ void *optr;
+ void *nptr;
+
+ saved_errno = errno;
+ memcpy(&optr, ptr, sizeof(ptr));
+ if (num == 0 || size == 0) {
+ free(optr);
+ nptr = NULL;
+ memcpy(ptr, &nptr, sizeof(ptr));
+ errno = saved_errno;
+ return 0;
+ }
+ if ((num >= 65535 || size >= 65535) && num > SIZE_MAX / size)
+ return EOVERFLOW;
+ nptr = realloc(optr, num * size);
+ if (nptr == NULL) {
+ result = errno;
+ } else {
+ result = 0;
+ memcpy(ptr, &nptr, sizeof(ptr));
+ }
+ errno = saved_errno;
+ return result;
+}
+
+#endif /* !HAVE_REALLOCARR && !HAVE_REALLOCARRAY */
diff --git a/usr.bin/doas/strtoi.c b/usr.bin/doas/strtoi.c
new file mode 100644
index 0000000..ec6481f
--- /dev/null
+++ b/usr.bin/doas/strtoi.c
@@ -0,0 +1,102 @@
+/* $NetBSD$ */
+/* NetBSD: _strtoi.h,v 1.2 2015/01/18 17:55:22 christos Exp */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Original version ID:
+ * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp
+ *
+ * Created by Kamil Rytarowski, based on ID:
+ * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joerg Exp
+ */
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+
+#include "doas.h"
+
+#if !defined(HAVE_STRTOI) && !defined(HAVE_STRTONUM)
+
+#if defined(HAVE_STRTOIMAX)
+#define STRTOIMAX strtoimax
+#elif defined(HAVE_STRTOLL)
+#define STRTOIMAX strtoll
+#else
+#define STRTOIMAX strtol
+#endif
+
+intmax_t
+strtoi(const char *nptr, char **endptr, int base, intmax_t lo, intmax_t hi,
+ int *rstatus)
+{
+ int serrno;
+ intmax_t im;
+ char *ep;
+ int rep;
+
+ if (endptr == NULL)
+ endptr = &ep;
+ if (rstatus == NULL)
+ rstatus = &rep;
+
+ serrno = errno;
+ errno = 0;
+
+ im = STRTOIMAX(nptr, endptr, base);
+
+ *rstatus = errno;
+ errno = serrno;
+
+ if (*rstatus == 0) {
+ /* No digits were found */
+ if (nptr == *endptr)
+ *rstatus = ECANCELED;
+ /* There are further characters after number */
+ else if (**endptr != '\0')
+ *rstatus = ENOTSUP;
+ }
+
+ if (im < lo) {
+ if (*rstatus == 0)
+ *rstatus = ERANGE;
+ return lo;
+ }
+ if (im > hi) {
+ if (*rstatus == 0)
+ *rstatus = ERANGE;
+ return hi;
+ }
+
+ return im;
+}
+
+#endif /* !HAVE_STRTOI && !HAVE_STRTONUM */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment