summaryrefslogtreecommitdiffstats
path: root/lib/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libutil')
-rw-r--r--lib/libutil/Makefile49
-rw-r--r--lib/libutil/_secure_path.375
-rw-r--r--lib/libutil/_secure_path.c74
-rw-r--r--lib/libutil/auth.356
-rw-r--r--lib/libutil/auth.c70
-rw-r--r--lib/libutil/auth.conf.535
-rw-r--r--lib/libutil/fparseln.3158
-rw-r--r--lib/libutil/fparseln.c222
-rw-r--r--lib/libutil/humanize_number.3148
-rw-r--r--lib/libutil/humanize_number.c148
-rw-r--r--lib/libutil/kld.398
-rw-r--r--lib/libutil/kld.c74
-rw-r--r--lib/libutil/libutil.h165
-rw-r--r--lib/libutil/login.367
-rw-r--r--lib/libutil/login.c75
-rw-r--r--lib/libutil/login.conf.5437
-rw-r--r--lib/libutil/login_auth.372
-rw-r--r--lib/libutil/login_auth.c108
-rw-r--r--lib/libutil/login_cap.3442
-rw-r--r--lib/libutil/login_cap.c808
-rw-r--r--lib/libutil/login_cap.h158
-rw-r--r--lib/libutil/login_class.3197
-rw-r--r--lib/libutil/login_class.c429
-rw-r--r--lib/libutil/login_crypt.c50
-rw-r--r--lib/libutil/login_ok.3142
-rw-r--r--lib/libutil/login_ok.c250
-rw-r--r--lib/libutil/login_times.3161
-rw-r--r--lib/libutil/login_times.c161
-rw-r--r--lib/libutil/login_tty.365
-rw-r--r--lib/libutil/login_tty.c62
-rw-r--r--lib/libutil/logout.369
-rw-r--r--lib/libutil/logout.c78
-rw-r--r--lib/libutil/logwtmp.371
-rw-r--r--lib/libutil/logwtmp.c104
-rw-r--r--lib/libutil/pidfile.3249
-rw-r--r--lib/libutil/pidfile.c246
-rw-r--r--lib/libutil/property.399
-rw-r--r--lib/libutil/property.c259
-rw-r--r--lib/libutil/pty.3149
-rw-r--r--lib/libutil/pty.c172
-rw-r--r--lib/libutil/pw_util.c616
-rw-r--r--lib/libutil/realhostname.3105
-rw-r--r--lib/libutil/realhostname.c186
-rw-r--r--lib/libutil/realhostname_sa.3133
-rw-r--r--lib/libutil/stub.c46
-rw-r--r--lib/libutil/trimdomain.385
-rw-r--r--lib/libutil/trimdomain.c115
-rw-r--r--lib/libutil/uucplock.3183
-rw-r--r--lib/libutil/uucplock.c230
49 files changed, 8251 insertions, 0 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
new file mode 100644
index 0000000..8da897e
--- /dev/null
+++ b/lib/libutil/Makefile
@@ -0,0 +1,49 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+LIB= util
+SHLIB_MAJOR= 5
+SHLIBDIR?= /lib
+CFLAGS+=-DLIBC_SCCS -I${.CURDIR} -I${.CURDIR}/../libc/gen/
+CFLAGS+=-DINET6
+SRCS= _secure_path.c auth.c fparseln.c humanize_number.c kld.c login.c \
+ login_auth.c login_cap.c login_class.c login_crypt.c login_ok.c \
+ login_times.c login_tty.c logout.c logwtmp.c \
+ pidfile.c property.c pty.c pw_util.c realhostname.c stub.c \
+ trimdomain.c uucplock.c
+INCS= libutil.h login_cap.h
+
+MAN+= kld.3 login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \
+ login_cap.3 login_class.3 login_times.3 login_ok.3 \
+ _secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \
+ realhostname_sa.3 trimdomain.3 fparseln.3 humanize_number.3 \
+ pidfile.3
+MAN+= login.conf.5 auth.conf.5
+MLINKS+= kld.3 kld_isloaded.3 kld.3 kld_load.3
+MLINKS+= property.3 properties_read.3 property.3 properties_free.3
+MLINKS+= property.3 property_find.3
+MLINKS+= auth.3 auth_getval.3
+MLINKS+= pty.3 openpty.3 pty.3 forkpty.3
+MLINKS+=login_cap.3 login_getclassbyname.3 login_cap.3 login_close.3 \
+ login_cap.3 login_getclass.3 login_cap.3 login_getuserclass.3 \
+ login_cap.3 login_getcapstr.3 login_cap.3 login_getcaplist.3 \
+ login_cap.3 login_getstyle.3 login_cap.3 login_getcaptime.3 \
+ login_cap.3 login_getcapnum.3 login_cap.3 login_getcapsize.3 \
+ login_cap.3 login_getcapbool.3 login_cap.3 login_getpath.3 \
+ login_cap.3 login_getpwclass.3 login_cap.3 login_setcryptfmt.3
+MLINKS+=login_class.3 setusercontext.3 login_class.3 setclasscontext.3 \
+ login_class.3 setclassenvironment.3 login_class.3 setclassresources.3
+MLINKS+=login_times.3 parse_lt.3 login_times.3 in_ltm.3 \
+ login_times.3 in_lt.3 login_times.3 in_ltms.3 \
+ login_times.3 in_lts.3
+MLINKS+=login_ok.3 auth_ttyok.3 login_ok.3 auth_hostok.3 \
+ login_ok.3 auth_timeok.3
+MLINKS+=login_auth.3 auth_checknologin.3 login_auth.3 auth_cat.3
+MLINKS+=uucplock.3 uu_lock.3 uucplock.3 uu_lock_txfr.3 \
+ uucplock.3 uu_unlock.3 uucplock.3 uu_lockerr.3
+MLINKS+=pidfile.3 pidfile_open.3 \
+ pidfile.3 pidfile_write.3 \
+ pidfile.3 pidfile_close.3 \
+ pidfile.3 pidfile_remove.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libutil/_secure_path.3 b/lib/libutil/_secure_path.3
new file mode 100644
index 0000000..fe23d12
--- /dev/null
+++ b/lib/libutil/_secure_path.3
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1997 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 2, 1997
+.Os
+.Dt _SECURE_PATH 3
+.Sh NAME
+.Nm _secure_path
+.Nd determine if a file appears to be secure
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft int
+.Fn _secure_path "const char *path" "uid_t uid" "gid_t gid"
+.Sh DESCRIPTION
+This function does some basic security checking on a given path.
+It is intended to be used by processes running with root privileges
+in order to decide whether or not to trust the contents of a given
+file.
+It uses a method often used to detect system compromise.
+.Pp
+A file is considered
+.Sq secure
+if it meets the following conditions:
+.Bl -enum
+.It
+The file exists, and is a regular file (not a symlink, device
+special or named pipe, etc.),
+.It
+Is not world writable.
+.It
+Is owned by the given uid or uid 0, if uid is not -1,
+.It
+Is not group writable or it has group ownership by the given
+gid, if gid is not -1.
+.El
+.Sh RETURN VALUES
+This function returns zero if the file exists and may be
+considered secure, -2 if the file does not exist, and
+-1 otherwise to indicate a security failure.
+The
+.Xr syslog 3
+function is used to log any failure of this function, including the
+reason, at LOG_ERR priority.
+.Sh SEE ALSO
+.Xr lstat 2 ,
+.Xr syslog 3
+.Sh HISTORY
+Code from which this function was derived was contributed to the
+.Fx
+project by Berkeley Software Design, Inc.
+.Sh BUGS
+The checks carried out are rudimentary and no attempt is made
+to eliminate race conditions between use of this function and
+access to the file referenced.
diff --git a/lib/libutil/_secure_path.c b/lib/libutil/_secure_path.c
new file mode 100644
index 0000000..363378b
--- /dev/null
+++ b/lib/libutil/_secure_path.c
@@ -0,0 +1,74 @@
+/*-
+ * Based on code copyright (c) 1995,1997 by
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <libutil.h>
+#include <stddef.h>
+#include <syslog.h>
+
+/*
+ * Check for common security problems on a given path
+ * It must be:
+ * 1. A regular file, and exists
+ * 2. Owned and writable only by root (or given owner)
+ * 3. Group ownership is given group or is non-group writable
+ *
+ * Returns: -2 if file does not exist,
+ * -1 if security test failure
+ * 0 otherwise
+ */
+
+int
+_secure_path(const char *path, uid_t uid, gid_t gid)
+{
+ int r = -1;
+ struct stat sb;
+ const char *msg = NULL;
+
+ if (lstat(path, &sb) < 0) {
+ if (errno == ENOENT) /* special case */
+ r = -2; /* if it is just missing, skip the log entry */
+ else
+ msg = "%s: cannot stat %s: %m";
+ }
+ else if (!S_ISREG(sb.st_mode))
+ msg = "%s: %s is not a regular file";
+ else if (sb.st_mode & S_IWOTH)
+ msg = "%s: %s is world writable";
+ else if ((int)uid != -1 && sb.st_uid != uid && sb.st_uid != 0) {
+ if (uid == 0)
+ msg = "%s: %s is not owned by root";
+ else
+ msg = "%s: %s is not owned by uid %d";
+ } else if ((int)gid != -1 && sb.st_gid != gid && (sb.st_mode & S_IWGRP))
+ msg = "%s: %s is group writeable by non-authorised groups";
+ else
+ r = 0;
+ if (msg != NULL)
+ syslog(LOG_ERR, msg, "_secure_path", path, uid);
+ return r;
+}
diff --git a/lib/libutil/auth.3 b/lib/libutil/auth.3
new file mode 100644
index 0000000..3a1bb4d
--- /dev/null
+++ b/lib/libutil/auth.3
@@ -0,0 +1,56 @@
+.\"
+.\" Copyright (c) 1998 Jordan Hubbard
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd October 7, 1998
+.Os
+.Dt AUTH_GETVAL 3
+.Sh NAME
+.Nm auth_getval
+.Nd functions for reading values from
+.Pa /etc/auth.conf
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft char *
+.Fn auth_getval "const char *name"
+.Sh DESCRIPTION
+The function
+.Fn auth_getval
+returns the value associated with the field called
+.Fa name
+or NULL if no such field is found or the auth file cannot be opened.
+.Sh FILES
+.Pa /etc/auth.conf
+contains the name=value pairs looked up by
+.Fn auth_getval .
+.Sh SEE ALSO
+.Xr properties_free 3 ,
+.Xr properties_read 3 ,
+.Xr property_find 3 ,
+.Xr auth.conf 5
diff --git a/lib/libutil/auth.c b/lib/libutil/auth.c
new file mode 100644
index 0000000..748c3ad
--- /dev/null
+++ b/lib/libutil/auth.c
@@ -0,0 +1,70 @@
+/*
+ * Simple authentication database handling code.
+ *
+ * Copyright (c) 1998
+ * Jordan Hubbard. 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,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 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 OR HIS PETS 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, LIFE 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <syslog.h>
+#include <unistd.h>
+
+static properties P;
+
+static int
+initauthconf(const char *path)
+{
+ int fd;
+
+ if (!P) {
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "initauthconf: unable to open file: %s", path);
+ return 1;
+ }
+ P = properties_read(fd);
+ close(fd);
+ if (!P) {
+ syslog(LOG_ERR, "initauthconf: unable to parse file: %s", path);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+char *
+auth_getval(const char *name)
+{
+ if (!P && initauthconf(_PATH_AUTHCONF))
+ return NULL;
+ else
+ return property_find(P, name);
+}
diff --git a/lib/libutil/auth.conf.5 b/lib/libutil/auth.conf.5
new file mode 100644
index 0000000..be10eb8
--- /dev/null
+++ b/lib/libutil/auth.conf.5
@@ -0,0 +1,35 @@
+.\" Copyright (c) 1998 Jordan Hubbard
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 7, 1998
+.Dt AUTH.CONF 5
+.Os
+.Sh NAME
+.Nm auth.conf
+.Nd authentication capability database
+.Sh SYNOPSIS
+.Pa /etc/auth.conf
+.Sh DESCRIPTION
+.Nm
+contains various attributes important to the authentication
+code, most notably
+.Xr crypt 3
+for the time being.
+This documentation will be updated as the
+.Pa /etc/auth.conf
+file, which is very new, evolves.
+.Sh SEE ALSO
+.Xr auth_getval 3 ,
+.Xr crypt 3
diff --git a/lib/libutil/fparseln.3 b/lib/libutil/fparseln.3
new file mode 100644
index 0000000..cd78437
--- /dev/null
+++ b/lib/libutil/fparseln.3
@@ -0,0 +1,158 @@
+.\" $NetBSD: fparseln.3,v 1.7 1999/07/02 15:49:12 simonb Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christos Zoulas.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" 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.
+.\"
+.Dd December 1, 1997
+.Dt FPARSELN 3
+.Os
+.Sh NAME
+.Nm fparseln
+.Nd return the next logical line from a stream
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In stdio.h
+.In libutil.h
+.Ft "char *"
+.Fo fparseln
+.Fa "FILE *stream" "size_t *len" "size_t *lineno"
+.Fa "const char delim[3]" "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn fparseln
+function
+returns a pointer to the next logical line from the stream referenced by
+.Fa stream .
+This string is
+.Dv NUL
+terminated and it is dynamically allocated on each invocation.
+It is the
+responsibility of the caller to free the pointer.
+.Pp
+By default, if a character is escaped, both it and the preceding escape
+character will be present in the returned string.
+Various
+.Fa flags
+alter this behaviour.
+.Pp
+The meaning of the arguments is as follows:
+.Bl -tag -width "lineno"
+.It Fa stream
+The stream to read from.
+.It Fa len
+If not
+.Dv NULL ,
+the length of the string is stored in the memory location to which it
+points.
+.It Fa lineno
+If not
+.Dv NULL ,
+the value of the memory location to which is pointed to, is incremented
+by the number of lines actually read from the file.
+.It Fa delim
+Contains the escape, continuation, and comment characters.
+If a character is
+.Dv NUL
+then processing for that character is disabled.
+If
+.Dv NULL ,
+all characters default to values specified below.
+The contents of
+.Fa delim
+is as follows:
+.Bl -tag -width "delim[0]"
+.It Fa delim[0]
+The escape character, which defaults to
+.Cm \e ,
+is used to remove any special meaning from the next character.
+.It Fa delim[1]
+The continuation character, which defaults to
+.Cm \e ,
+is used to indicate that the next line should be concatenated with the
+current one if this character is the last character on the current line
+and is not escaped.
+.It Fa delim[2]
+The comment character, which defaults to
+.Cm # ,
+if not escaped indicates the beginning of a comment that extends until the
+end of the current line.
+.El
+.It Fa flags
+If non-zero, alter the operation of
+.Fn fparseln .
+The various flags, which may be
+.Em or Ns -ed
+together, are:
+.Bl -tag -width "FPARSELN_UNESCCOMM"
+.It Dv FPARSELN_UNESCCOMM
+Remove escape preceding an escaped comment.
+.It Dv FPARSELN_UNESCCONT
+Remove escape preceding an escaped continuation.
+.It Dv FPARSELN_UNESCESC
+Remove escape preceding an escaped escape.
+.It Dv FPARSELN_UNESCREST
+Remove escape preceding any other character.
+.It Dv FPARSELN_UNESCALL
+All of the above.
+.El
+.Pp
+.El
+.Sh RETURN VALUES
+Upon successful completion a pointer to the parsed line is returned;
+otherwise,
+.Dv NULL
+is returned.
+.Pp
+The
+.Fn fparseln
+function uses internally
+.Xr fgetln 3 ,
+so all error conditions that apply to
+.Xr fgetln 3 ,
+apply to
+.Fn fparseln .
+In addition
+.Fn fparseln
+may set
+.Va errno
+to
+.Er ENOMEM
+and return
+.Dv NULL
+if it runs out of memory.
+.Sh SEE ALSO
+.Xr fgetln 3
+.Sh HISTORY
+The
+.Fn fparseln
+function first appeared in
+.Nx 1.4
+and
+.Fx 4.0 .
diff --git a/lib/libutil/fparseln.c b/lib/libutil/fparseln.c
new file mode 100644
index 0000000..0624f0e
--- /dev/null
+++ b/lib/libutil/fparseln.c
@@ -0,0 +1,222 @@
+/* $NetBSD: fparseln.c,v 1.9 1999/09/20 04:48:06 lukem Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libutil.h>
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ * Return true if the character in *p that belongs to a string
+ * that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+ const char *cp;
+ size_t ne;
+
+#if 0
+ _DIAGASSERT(sp != NULL);
+ _DIAGASSERT(p != NULL);
+#endif
+
+ /* No escape character */
+ if (esc == '\0')
+ return 1;
+
+ /* Count the number of escape characters that precede ours */
+ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+ continue;
+
+ /* Return true if odd number of escape characters */
+ return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ * Read a line from a file parsing continuations ending in \
+ * and eliminating trailing newlines, or comments starting with
+ * the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags)
+{
+ static const char dstr[3] = { '\\', '\\', '#' };
+
+ size_t s, len;
+ char *buf;
+ char *ptr, *cp;
+ int cnt;
+ char esc, con, nl, com;
+
+#if 0
+ _DIAGASSERT(fp != NULL);
+#endif
+
+ len = 0;
+ buf = NULL;
+ cnt = 1;
+
+ if (str == NULL)
+ str = dstr;
+
+ esc = str[0];
+ con = str[1];
+ com = str[2];
+ /*
+ * XXX: it would be cool to be able to specify the newline character,
+ * but unfortunately, fgetln does not let us
+ */
+ nl = '\n';
+
+ while (cnt) {
+ cnt = 0;
+
+ if (lineno)
+ (*lineno)++;
+
+ if ((ptr = fgetln(fp, &s)) == NULL)
+ break;
+
+ if (s && com) { /* Check and eliminate comments */
+ for (cp = ptr; cp < ptr + s; cp++)
+ if (*cp == com && !isescaped(ptr, cp, esc)) {
+ s = cp - ptr;
+ cnt = s == 0 && buf == NULL;
+ break;
+ }
+ }
+
+ if (s && nl) { /* Check and eliminate newlines */
+ cp = &ptr[s - 1];
+
+ if (*cp == nl)
+ s--; /* forget newline */
+ }
+
+ if (s && con) { /* Check and eliminate continuations */
+ cp = &ptr[s - 1];
+
+ if (*cp == con && !isescaped(ptr, cp, esc)) {
+ s--; /* forget escape */
+ cnt = 1;
+ }
+ }
+
+ if (s == 0 && buf != NULL)
+ continue;
+
+ if ((cp = realloc(buf, len + s + 1)) == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = cp;
+
+ (void) memcpy(buf + len, ptr, s);
+ len += s;
+ buf[len] = '\0';
+ }
+
+ if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+ strchr(buf, esc) != NULL) {
+ ptr = cp = buf;
+ while (cp[0] != '\0') {
+ int skipesc;
+
+ while (cp[0] != '\0' && cp[0] != esc)
+ *ptr++ = *cp++;
+ if (cp[0] == '\0' || cp[1] == '\0')
+ break;
+
+ skipesc = 0;
+ if (cp[1] == com)
+ skipesc += (flags & FPARSELN_UNESCCOMM);
+ if (cp[1] == con)
+ skipesc += (flags & FPARSELN_UNESCCONT);
+ if (cp[1] == esc)
+ skipesc += (flags & FPARSELN_UNESCESC);
+ if (cp[1] != com && cp[1] != con && cp[1] != esc)
+ skipesc = (flags & FPARSELN_UNESCREST);
+
+ if (skipesc)
+ cp++;
+ else
+ *ptr++ = *cp++;
+ *ptr++ = *cp++;
+ }
+ *ptr = '\0';
+ len = strlen(buf);
+ }
+
+ if (size)
+ *size = len;
+ return buf;
+}
+
+#ifdef TEST
+
+int
+main(int argc, char *argv[])
+{
+ char *ptr;
+ size_t size, line;
+
+ line = 0;
+ while ((ptr = fparseln(stdin, &size, &line, NULL,
+ FPARSELN_UNESCALL)) != NULL)
+ printf("line %d (%d) |%s|\n", line, size, ptr);
+ return 0;
+}
+
+/*
+
+# This is a test
+line 1
+line 2 \
+line 3 # Comment
+line 4 \# Not comment \\\\
+
+# And a comment \
+line 5 \\\
+line 6
+
+*/
+
+#endif /* TEST */
diff --git a/lib/libutil/humanize_number.3 b/lib/libutil/humanize_number.3
new file mode 100644
index 0000000..650c936
--- /dev/null
+++ b/lib/libutil/humanize_number.3
@@ -0,0 +1,148 @@
+.\" $NetBSD: humanize_number.3,v 1.4 2003/04/16 13:34:37 wiz Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 1999, 2002 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Luke Mewburn and by Tomas Svensson.
+.\"
+.\" 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd May 25, 2004
+.Dt HUMANIZE_NUMBER 3
+.Os
+.Sh NAME
+.Nm humanize_number
+.Nd format a number into a human readable form
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fo humanize_number
+.Fa "char *buf" "size_t len" "int64_t number" "const char *suffix"
+.Fa "int scale" "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn humanize_number
+function formats the signed 64-bit quantity given in
+.Fa number
+into
+.Fa buffer .
+A space and then
+.Fa suffix
+is appended to the end.
+The buffer pointed to by
+.Fa buffer
+must be at least
+.Fa len
+bytes long.
+.Pp
+If the formatted number (including
+.Fa suffix )
+would be too long to fit into
+.Fa buffer ,
+then divide
+.Fa number
+by 1024 until it will.
+In this case, prefix
+.Fa suffix
+with the appropriate SI designator.
+.Pp
+The prefixes are:
+.Bl -column "Prefix" "Description" "Multiplier" -offset indent
+.It Sy "Prefix" Ta Sy "Description" Ta Sy "Multiplier"
+.It Li k Ta No kilo Ta 1024
+.It Li M Ta No mega Ta 1048576
+.It Li G Ta No giga Ta 1073741824
+.It Li T Ta No tera Ta 1099511627776
+.It Li P Ta No peta Ta 1125899906842624
+.It Li E Ta No exa Ta 1152921504606846976
+.El
+.Pp
+The
+.Fa len
+argument must be at least 4 plus the length of
+.Fa suffix ,
+in order to ensure a useful result is generated into
+.Fa buffer .
+To use a specific prefix, specify this as
+.Fa scale
+(multiplier = 1024 ^ scale).
+This cannot be combined with any of the
+.Fa scale
+flags below.
+.Pp
+The following flags may be passed in
+.Fa scale :
+.Bl -tag -width ".Dv HN_DIVISOR_1000" -offset indent
+.It Dv HN_AUTOSCALE
+Format the buffer using the lowest multiplier possible.
+.It Dv HN_GETSCALE
+Return the prefix index number (the number of times
+.Fa number
+must be divided to fit) instead of formatting it to the buffer.
+.El
+.Pp
+The following flags may be passed in
+.Fa flags :
+.Bl -tag -width ".Dv HN_DIVISOR_1000" -offset indent
+.It Dv HN_DECIMAL
+If the final result is less than 10, display it using one digit.
+.It Dv HN_NOSPACE
+Do not put a space between
+.Fa number
+and the prefix.
+.It Dv HN_B
+Use
+.Ql B
+(bytes) as prefix if the original result does not have a prefix.
+.It Dv HN_DIVISOR_1000
+Divide
+.Fa number
+with 1000 instead of 1024.
+.El
+.Sh RETURN VALUES
+The
+.Fn humanize_number
+function returns the number of characters stored in
+.Fa buffer
+(excluding the terminating
+.Dv NUL )
+upon success, or \-1 upon failure.
+If
+.Dv HN_GETSCALE
+is specified, the prefix index number will be returned instead.
+.Sh HISTORY
+The
+.Fn humanize_number
+function first appeared in
+.Nx 2.0 .
diff --git a/lib/libutil/humanize_number.c b/lib/libutil/humanize_number.c
new file mode 100644
index 0000000..21d5e39
--- /dev/null
+++ b/lib/libutil/humanize_number.c
@@ -0,0 +1,148 @@
+/* $NetBSD: humanize_number.c,v 1.8 2004/07/27 01:56:24 enami Exp $ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <libutil.h>
+
+int
+humanize_number(char *buf, size_t len, int64_t bytes,
+ const char *suffix, int scale, int flags)
+{
+ const char *prefixes, *sep;
+ int b, i, r, maxscale, s1, s2, sign;
+ int64_t divisor, max;
+ size_t baselen;
+
+ assert(buf != NULL);
+ assert(suffix != NULL);
+ assert(scale >= 0);
+
+ if (flags & HN_DIVISOR_1000) {
+ /* SI for decimal multiplies */
+ divisor = 1000;
+ if (flags & HN_B)
+ prefixes = "B\0k\0M\0G\0T\0P\0E";
+ else
+ prefixes = "\0\0k\0M\0G\0T\0P\0E";
+ } else {
+ /*
+ * binary multiplies
+ * XXX IEC 60027-2 recommends Ki, Mi, Gi...
+ */
+ divisor = 1024;
+ if (flags & HN_B)
+ prefixes = "B\0K\0M\0G\0T\0P\0E";
+ else
+ prefixes = "\0\0K\0M\0G\0T\0P\0E";
+ }
+
+#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1])
+ maxscale = 7;
+
+ if (scale >= maxscale &&
+ (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
+ return (-1);
+
+ if (buf == NULL || suffix == NULL)
+ return (-1);
+
+ if (len > 0)
+ buf[0] = '\0';
+ if (bytes < 0) {
+ sign = -1;
+ bytes *= -100;
+ baselen = 3; /* sign, digit, prefix */
+ } else {
+ sign = 1;
+ bytes *= 100;
+ baselen = 2; /* digit, prefix */
+ }
+ if (flags & HN_NOSPACE)
+ sep = "";
+ else {
+ sep = " ";
+ baselen++;
+ }
+ baselen += strlen(suffix);
+
+ /* Check if enough room for `x y' + suffix + `\0' */
+ if (len < baselen + 1)
+ return (-1);
+
+ if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
+ /* See if there is additional columns can be used. */
+ for (max = 100, i = len - baselen; i-- > 0;)
+ max *= 10;
+
+ for (i = 0; bytes >= max && i < maxscale; i++)
+ bytes /= divisor;
+
+ if (scale & HN_GETSCALE)
+ return (i);
+ } else
+ for (i = 0; i < scale && i < maxscale; i++)
+ bytes /= divisor;
+
+ /* If a value <= 9.9 after rounding and ... */
+ if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
+ /* baselen + \0 + .N */
+ if (len < baselen + 1 + 2)
+ return (-1);
+ b = ((int)bytes + 5) / 10;
+ s1 = b / 10;
+ s2 = b % 10;
+ r = snprintf(buf, len, "%d%s%d%s%s%s",
+ sign * s1, localeconv()->decimal_point, s2,
+ sep, SCALE2PREFIX(i), suffix);
+ } else
+ r = snprintf(buf, len, "%lld%s%s%s",
+ /* LONGLONG */
+ (long long)(sign * ((bytes + 50) / 100)),
+ sep, SCALE2PREFIX(i), suffix);
+
+ return (r);
+}
diff --git a/lib/libutil/kld.3 b/lib/libutil/kld.3
new file mode 100644
index 0000000..c3cc3a7
--- /dev/null
+++ b/lib/libutil/kld.3
@@ -0,0 +1,98 @@
+.\"-
+.\" Copyright (c) 2006 Dag-Erling Coïdan Smørgrav
+.\" 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 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 18, 2006
+.Os
+.Dt KLD 3
+.Sh NAME
+.Nm kld_isloaded ,
+.Nm kld_load
+.Nd kld utility functions
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fn kld_isloaded "const char *name"
+.Ft int
+.Fn kld_load "const char *name"
+.Sh DESCRIPTION
+These functions facilitate loading kernel modules from userland
+applications.
+.Pp
+The
+.Fn kld_isloaded
+function takes a name and returns a non-zero value if a module of that
+name is currently loaded.
+The name can be either the name of a module file
+.Po
+.Va cpufreq.ko
+.Pc ,
+the same name without the
+.Pa .ko
+extension
+.Po
+.Va cpufreq
+.Pc ,
+or the name of a module contained within that file
+.Po
+.Va cpu/ichss
+.Pc .
+Only the latter will return correct results if the module is compiled
+into the kernel.
+.Pp
+The
+.Fn kld_load
+function is a simple wrapper around the
+.Fn kldload
+function.
+It returns zero if and only if the corresponding
+.Fn kldload
+call succeeded or returned
+.Er EEXIST
+(signifying that the requested module was already loaded).
+.Sh SEE ALSO
+.Xr kldfirstmod 2
+.Xr kldload 2 ,
+.Xr kldnext 2 ,
+.Xr kldstat 2 ,
+.Xr modfnext 2 ,
+.Xr modstat 2
+.Sh HISTORY
+The
+.Fn kld_isloaded
+and
+.Fn kld_load
+functions first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+The
+.Fn kld_isloaded
+and
+.Fn kld_load
+functions and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
diff --git a/lib/libutil/kld.c b/lib/libutil/kld.c
new file mode 100644
index 0000000..b8aa9ef
--- /dev/null
+++ b/lib/libutil/kld.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2006 Dag-Erling Coïdan Smørgrav
+ * 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
+ * in this position and unchanged.
+ * 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 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <errno.h>
+
+int
+kld_isloaded(const char *name)
+{
+ struct kld_file_stat fstat;
+ struct module_stat mstat;
+ const char *ko;
+ int fid, mid;
+
+ for (fid = kldnext(0); fid > 0; fid = kldnext(fid)) {
+ fstat.version = sizeof(fstat);
+ if (kldstat(fid, &fstat) != 0)
+ continue;
+ /* check if the file name matches the supplied name */
+ if (strcmp(fstat.name, name) == 0)
+ return (1);
+ /* strip .ko and try again */
+ if ((ko = strstr(fstat.name, ".ko")) != NULL &&
+ strlen(name) == ko - fstat.name &&
+ strncmp(fstat.name, name, ko - fstat.name) == 0)
+ return (1);
+ /* look for a matching module within the file */
+ for (mid = kldfirstmod(fid); mid > 0; mid = modfnext(mid)) {
+ mstat.version = sizeof(mstat);
+ if (modstat(mid, &mstat) != 0)
+ continue;
+ if (strcmp(mstat.name, name) == 0)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+kld_load(const char *name)
+{
+ if (kldload(name) == -1 && errno != EEXIST)
+ return (-1);
+ return (0);
+}
diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h
new file mode 100644
index 0000000..aefff0c
--- /dev/null
+++ b/lib/libutil/libutil.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1996 Peter Wemm <peter@FreeBSD.org>.
+ * All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is 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. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LIBUTIL_H_
+#define _LIBUTIL_H_
+
+#define PROPERTY_MAX_NAME 64
+#define PROPERTY_MAX_VALUE 512
+
+/* for properties.c */
+typedef struct _property {
+ struct _property *next;
+ char *name;
+ char *value;
+} *properties;
+
+#ifdef _SYS_PARAM_H_
+/* for pidfile.c */
+struct pidfh {
+ int pf_fd;
+ char pf_path[MAXPATHLEN + 1];
+ __dev_t pf_dev;
+ ino_t pf_ino;
+};
+#endif
+
+/* Avoid pulling in all the include files for no need */
+struct termios;
+struct winsize;
+struct utmp;
+struct in_addr;
+
+__BEGIN_DECLS
+void clean_environment(const char * const *_white,
+ const char * const *_more_white);
+int extattr_namespace_to_string(int _attrnamespace, char **_string);
+int extattr_string_to_namespace(const char *_string, int *_attrnamespace);
+void login(struct utmp *_ut);
+int login_tty(int _fd);
+int logout(const char *_line);
+void logwtmp(const char *_line, const char *_name, const char *_host);
+void trimdomain(char *_fullhost, int _hostsize);
+int openpty(int *_amaster, int *_aslave, char *_name,
+ struct termios *_termp, struct winsize *_winp);
+int forkpty(int *_amaster, char *_name,
+ struct termios *_termp, struct winsize *_winp);
+int humanize_number(char *_buf, size_t _len, int64_t _number,
+ const char *_suffix, int _scale, int _flags);
+const char *uu_lockerr(int _uu_lockresult);
+int uu_lock(const char *_ttyname);
+int uu_unlock(const char *_ttyname);
+int uu_lock_txfr(const char *_ttyname, pid_t _pid);
+int _secure_path(const char *_path, uid_t _uid, gid_t _gid);
+properties properties_read(int fd);
+void properties_free(properties list);
+char *property_find(properties list, const char *name);
+char *auth_getval(const char *name);
+int realhostname(char *host, size_t hsize, const struct in_addr *ip);
+struct sockaddr;
+int realhostname_sa(char *host, size_t hsize, struct sockaddr *addr,
+ int addrlen);
+
+int kld_isloaded(const char *name);
+int kld_load(const char *name);
+
+#ifdef _STDIO_H_ /* avoid adding new includes */
+char *fparseln(FILE *, size_t *, size_t *, const char[3], int);
+#endif
+
+#ifdef _PWD_H_
+int pw_copy(int _ffd, int _tfd, const struct passwd *_pw, struct passwd *_old_pw);
+struct passwd *pw_dup(const struct passwd *_pw);
+int pw_edit(int _notsetuid);
+int pw_equal(const struct passwd *_pw1, const struct passwd *_pw2);
+void pw_fini(void);
+int pw_init(const char *_dir, const char *_master);
+char *pw_make(const struct passwd *_pw);
+int pw_mkdb(const char *_user);
+int pw_lock(void);
+struct passwd *pw_scan(const char *_line, int _flags);
+const char *pw_tempname(void);
+int pw_tmp(int _mfd);
+#endif
+
+#ifdef _SYS_PARAM_H_
+struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr);
+int pidfile_write(struct pidfh *pfh);
+int pidfile_close(struct pidfh *pfh);
+int pidfile_remove(struct pidfh *pfh);
+#endif
+
+__END_DECLS
+
+#define UU_LOCK_INUSE (1)
+#define UU_LOCK_OK (0)
+#define UU_LOCK_OPEN_ERR (-1)
+#define UU_LOCK_READ_ERR (-2)
+#define UU_LOCK_CREAT_ERR (-3)
+#define UU_LOCK_WRITE_ERR (-4)
+#define UU_LOCK_LINK_ERR (-5)
+#define UU_LOCK_TRY_ERR (-6)
+#define UU_LOCK_OWNER_ERR (-7)
+
+/* return values from realhostname() */
+#define HOSTNAME_FOUND (0)
+#define HOSTNAME_INCORRECTNAME (1)
+#define HOSTNAME_INVALIDADDR (2)
+#define HOSTNAME_INVALIDNAME (3)
+
+/* fparseln(3) */
+#define FPARSELN_UNESCESC 0x01
+#define FPARSELN_UNESCCONT 0x02
+#define FPARSELN_UNESCCOMM 0x04
+#define FPARSELN_UNESCREST 0x08
+#define FPARSELN_UNESCALL 0x0f
+
+/* pw_scan() */
+#define PWSCAN_MASTER 0x01
+#define PWSCAN_WARN 0x02
+
+/* humanize_number(3) */
+#define HN_DECIMAL 0x01
+#define HN_NOSPACE 0x02
+#define HN_B 0x04
+#define HN_DIVISOR_1000 0x08
+
+#define HN_GETSCALE 0x10
+#define HN_AUTOSCALE 0x20
+
+#endif /* !_LIBUTIL_H_ */
diff --git a/lib/libutil/login.3 b/lib/libutil/login.3
new file mode 100644
index 0000000..d88f719
--- /dev/null
+++ b/lib/libutil/login.3
@@ -0,0 +1,67 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Os
+.Dt LOGIN 3
+.Sh NAME
+.Nm login
+.Nd "log a new login record to the utmp and wtmp files"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In utmp.h
+.In libutil.h
+.Ft void
+.Fn login "struct utmp *ut"
+.Sh DESCRIPTION
+The function
+.Fn login
+records the
+.Ar ut
+entry being passed into the appropriate slot of the
+.Xr utmp 5
+file,
+and appends it to the
+.Xr wtmp 5
+file.
+The calling process must have permission to write to both files.
+.Sh RETURN VALUES
+None.
+.Sh SEE ALSO
+.Xr logout 3 ,
+.Xr ttyslot 3 ,
+.Xr utmp 5 ,
+.Xr wtmp 5
+.Sh BUGS
+The interface provided by
+.Fn login
+is rather crude.
+The caller must know about the details of a
+.Va struct utmp .
+Some better abstraction needs to be worked out.
diff --git a/lib/libutil/login.c b/lib/libutil/login.c
new file mode 100644
index 0000000..c2a0b16
--- /dev/null
+++ b/lib/libutil/login.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)login.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ttyent.h>
+#include <unistd.h>
+#include <utmp.h>
+
+void
+login(struct utmp *ut)
+{
+ struct ttyent *ty;
+ int fd;
+ int tty;
+
+ setttyent();
+ for (tty = 1; (ty = getttyent()) != NULL; ++tty)
+ if (strcmp(ty->ty_name, ut->ut_line) == 0)
+ break;
+ endttyent();
+ if (tty > 0 && (fd = open(_PATH_UTMP, O_WRONLY|O_CREAT, 0644)) >= 0) {
+ (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), L_SET);
+ (void)write(fd, ut, sizeof(struct utmp));
+ (void)close(fd);
+ }
+ if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) {
+ (void)write(fd, ut, sizeof(struct utmp));
+ (void)close(fd);
+ }
+}
diff --git a/lib/libutil/login.conf.5 b/lib/libutil/login.conf.5
new file mode 100644
index 0000000..01f9a9f
--- /dev/null
+++ b/lib/libutil/login.conf.5
@@ -0,0 +1,437 @@
+.\" Copyright (c) 1996 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 19, 2006
+.Dt LOGIN.CONF 5
+.Os
+.Sh NAME
+.Nm login.conf
+.Nd login class capability database
+.Sh SYNOPSIS
+.Pa /etc/login.conf ,
+.Pa ~/.login_conf
+.Sh DESCRIPTION
+.Nm
+contains various attributes and capabilities of login classes.
+A login class (an optional annotation against each record in the user
+account database,
+.Pa /etc/master.passwd )
+determines session accounting, resource limits and user environment settings.
+It is used by various programs in the system to set up a user's login
+environment and to enforce policy, accounting and administrative restrictions.
+It also provides the means by which users are able to be
+authenticated to the system and the types of authentication available.
+Attributes in addition to the ones described here are available with
+third-party packages.
+.Pp
+A special record "default" in the system user class capability database
+.Pa /etc/login.conf
+is used automatically for any
+non-root user without a valid login class in
+.Pa /etc/master.passwd .
+A user with a uid of 0 without a valid login class will use the record
+"root" if it exists, or "default" if not.
+.Pp
+In
+.Fx ,
+users may individually create a file called
+.Pa .login_conf
+in their home directory using the same format, consisting of a single
+entry with a record id of "me".
+If present, this file is used by
+.Xr login 1
+to set user-defined environment settings which override those specified
+in the system login capabilities database.
+Only a subset of login capabilities may be overridden, typically those
+which do not involve authentication, resource limits and accounting.
+.Pp
+Records in a class capabilities database consist of a number of
+colon-separated fields.
+The first entry for each record gives one or more names that a record is
+to be known by, each separated by a '|' character.
+The first name is the most common abbreviation.
+The last name given should be a long name that is more descriptive
+of the capability entry, and all others are synonyms.
+All names but the last should be in lower case and contain no blanks;
+the last name may contain upper case characters and blanks for
+readability.
+.Pp
+The default
+.Pa /etc/login.conf
+shipped with
+.Fx
+is an out of the box configuration.
+Whenever changes to this, or
+the user's
+.Pa ~/.login_conf ,
+file are made, the modifications will not be picked up until
+.Xr cap_mkdb 1
+is used to compile the file into a database.
+This database file will have a
+.Pa .db
+extension and is accessed through
+.Xr cgetent 3 .
+See
+.Xr getcap 3
+for a more in-depth description of the format of a capability database.
+.Sh CAPABILITIES
+Fields within each record in the database follow the
+.Xr getcap 3
+conventions for boolean, type string
+.Ql \&=
+and type numeric
+.Ql \&# ,
+although type numeric is deprecated in favour of the string format and
+either form is accepted for a numeric datum.
+Values fall into the following categories:
+.Bl -tag -width "program"
+.It bool
+If the name is present, then the boolean value is true; otherwise, it is
+false
+.It file
+Path name to a data file
+.It program
+Path name to an executable file
+.It list
+A list of values (or pairs of values) separated by commas or spaces
+.It path
+A space or comma separated list of path names, following the usual csh
+conventions (leading tilde with and without username being expanded to
+home directories etc.)
+.It number
+A numeric value, either decimal (default), hexadecimal (with leading 0x),
+or octal (with a leading 0).
+With a numeric type, only one numeric value is allowed.
+Numeric types may also be specified in string format (i.e., the capability
+tag being delimited from the value by '=' instead of '#').
+Whichever method is used, then all records in the database must use the
+same method to allow values to be correctly overridden in interpolated
+records.
+.It size
+A number which expresses a size.
+The default interpretation of a value is the number of bytes, but a
+suffix may specify alternate units:
+.Bl -tag -offset indent -compact -width xxxx
+.It b
+explicitly selects 512-byte blocks
+.It k
+selects kilobytes (1024 bytes)
+.It m
+specifies a multiplier of 1 megabyte (1048576 bytes),
+.It g
+specifies units of gigabytes, and
+.It t
+represents terabytes.
+.El
+A size value is a numeric quantity and case of the suffix is not significant.
+Concatenated values are added together.
+.It time
+A period of time, by default in seconds.
+A prefix may specify a different unit:
+.Bl -tag -offset indent -compact -width xxxx
+.It y
+indicates the number of 365 day years,
+.It w
+indicates the number of weeks,
+.It d
+the number of days,
+.It h
+the number of hours,
+.It m
+the number of minutes, and
+.It s
+the number of seconds.
+.El
+Concatenated values are added together.
+For example, 2 hours and 40 minutes may be written either as
+9600s, 160m or 2h40m.
+.El
+.Pp
+The usual convention to interpolate capability entries using the special
+.Em tc=value
+notation may be used.
+.Sh RESOURCE LIMITS
+.Bl -column coredumpsize indent indent
+.It Sy "Name Type Notes Description
+.It "coredumpsize size Maximum coredump size limit.
+.It "cputime time CPU usage limit.
+.It "datasize size Maximum data size limit.
+.It "filesize size Maximum file size limit.
+.It "maxproc number Maximum number of processes.
+.It "memorylocked size Maximum locked in core memory size limit.
+.It "memoryuse size Maximum of core memory use size limit.
+.It "openfiles number Maximum number of open files per process.
+.It "sbsize size Maximum permitted socketbuffer size.
+.It "vmemoryuse size Maximum permitted total VM usage per process.
+.It "stacksize size Maximum stack size limit.
+.El
+.Pp
+These resource limit entries actually specify both the maximum
+and current limits (see
+.Xr getrlimit 2 ) .
+The current (soft) limit is the one normally used, although the user is
+permitted to increase the current limit to the maximum (hard) limit.
+The maximum and current limits may be specified individually by appending a
+-max or -cur to the capability name.
+.Sh ENVIRONMENT
+.Bl -column ignorenologin indent xbinxxusrxbin
+.It Sy "Name Type Notes Description
+.It "charset string Set $MM_CHARSET environment variable to the specified
+value.
+.It "hushlogin bool false Same as having a ~/.hushlogin file.
+.It "ignorenologin bool false Login not prevented by nologin.
+.It "ftp-chroot bool false Limit FTP access with
+.Xr chroot 2
+to the
+.Ev HOME
+directory of the user.
+See
+.Xr ftpd 8
+for details.
+.It "label string Default MAC policy; see
+.Xr maclabel 7 .
+.It "lang string Set $LANG environment variable to the specified value.
+.It "manpath path Default search path for manpages.
+.It "nocheckmail bool false Display mail status at login.
+.It "nologin file If the file exists it will be displayed and
+the login session will be terminated.
+.It "path path /bin /usr/bin Default search path.
+.It "priority number Initial priority (nice) level.
+.It "requirehome bool false Require a valid home directory to login.
+.It "setenv list A comma-separated list of environment variables and
+values to which they are to be set.
+.It "shell prog Session shell to execute rather than the
+shell specified in the passwd file.
+The SHELL environment variable will
+contain the shell specified in the password file.
+.It "term string Default terminal type if not able to determine
+from other means.
+.It "timezone string Default value of $TZ environment variable.
+.It "umask number 022 Initial umask. Should always have a leading 0 to
+ensure octal interpretation.
+.It "welcome file /etc/motd File containing welcome message.
+.El
+.Sh AUTHENTICATION
+.Bl -column passwd_prompt indent indent
+.It Sy "Name Type Notes Description
+.\" .It "approve program Program to approve login.
+.It "copyright file File containing additional copyright information
+.It "host.allow list List of remote host wildcards from which users in
+the class may access.
+.It "host.deny list List of remote host wildcards from which users
+in the class may not access.
+.It "login_prompt string The login prompt given by
+.Xr login 1
+.It "login-backoff number 3 The number of login attempts
+allowed before the backoff delay is inserted after each subsequent
+attempt.
+The backoff delay is the number of tries above
+.Em login-backoff
+multiplied by 5 seconds.
+.It "login-retries number 10 The number of login attempts
+allowed before the login fails.
+.It "passwd_format string md5 The encryption format that new or
+changed passwords will use.
+Valid values include "des", "md5" and "blf".
+NIS clients using a
+.No non- Ns Fx
+NIS server should probably use "des".
+.It "passwd_prompt string The password prompt presented by
+.Xr login 1
+.It "times.allow list List of time periods during which
+logins are allowed.
+.It "times.deny list List of time periods during which logins are
+disallowed.
+.It "ttys.allow list List of ttys and ttygroups which users
+in the class may use for access.
+.It "ttys.deny list List of ttys and ttygroups which users
+in the class may not use for access.
+.It "warnexpire time Advance notice for pending account expiry.
+.It "warnpassword time Advance notice for pending password expiry.
+.\".It "widepasswords bool false Use the wide password format. The wide password
+.\" format allows up to 128 significant characters in the password.
+.El
+.Pp
+These fields are intended to be used by
+.Xr passwd 1
+and other programs in the login authentication system.
+.Pp
+Capabilities that set environment variables are scanned for both
+.Ql \&~
+and
+.Ql \&$
+characters, which are substituted for a user's home directory and name
+respectively.
+To pass these characters literally into the environment variable, escape
+the character by preceding it with a backslash '\\'.
+.Pp
+The
+.Em host.allow
+and
+.Em host.deny
+entries are comma separated lists used for checking remote access to the system,
+and consist of a list of hostnames and/or IP addresses against which remote
+network logins are checked.
+Items in these lists may contain wildcards in the form used by shell programs
+for wildcard matching (See
+.Xr fnmatch 3
+for details on the implementation).
+The check on hosts is made against both the remote system's Internet address
+and hostname (if available).
+If both lists are empty or not specified, then logins from any remote host
+are allowed.
+If host.allow contains one or more hosts, then only remote systems matching
+any of the items in that list are allowed to log in.
+If host.deny contains one or more hosts, then a login from any matching hosts
+will be disallowed.
+.Pp
+The
+.Em times.allow
+and
+.Em times.deny
+entries consist of a comma-separated list of time periods during which the users
+in a class are allowed to be logged in.
+These are expressed as one or more day codes followed by a start and end times
+expressed in 24 hour format, separated by a hyphen or dash.
+For example, MoThSa0200-1300 translates to Monday, Thursday and Saturday between
+the hours of 2 am and 1 p.m..
+If both of these time lists are empty, users in the class are allowed access at
+any time.
+If
+.Em times.allow
+is specified, then logins are only allowed during the periods given.
+If
+.Em times.deny
+is specified, then logins are denied during the periods given, regardless of whether
+one of the periods specified in
+.Em times.allow
+applies.
+.Pp
+Note that
+.Xr login 1
+enforces only that the actual login falls within periods allowed by these entries.
+Further enforcement over the life of a session requires a separate daemon to
+monitor transitions from an allowed period to a non-allowed one.
+.Pp
+The
+.Em ttys.allow
+and
+.Em ttys.deny
+entries contain a comma-separated list of tty devices (without the /dev/ prefix)
+that a user in a class may use to access the system, and/or a list of ttygroups
+(See
+.Xr getttyent 3
+and
+.Xr ttys 5
+for information on ttygroups).
+If neither entry exists, then the choice of login device used by the user is
+unrestricted.
+If only
+.Em ttys.allow
+is specified, then the user is restricted only to ttys in the given
+group or device list.
+If only
+.Em ttys.deny
+is specified, then the user is prevented from using the specified devices or
+devices in the group.
+If both lists are given and are non-empty, the user is restricted to those
+devices allowed by ttys.allow that are not available by ttys.deny.
+.Pp
+The
+.Em minpasswordlen
+and
+.Em minpasswordcase
+facilities for enforcing restrictions on password quality, which used
+to be supported by
+.Nm ,
+have been superseded by the
+.Xr pam_passwdqc 8
+PAM module.
+.Sh RESERVED CAPABILITIES
+The following capabilities are reserved for the purposes indicated and
+may be supported by third-party software.
+They are not implemented in the base system.
+.Bl -column host.accounted indent indent
+.It Sy "Name Type Notes Description
+.It "accounted bool false Enable session time accounting for all users
+in this class.
+.It "autodelete time Time after expiry when account is auto-deleted.
+.It "bootfull bool false Enable 'boot only if ttygroup is full' strategy
+when terminating sessions.
+.It "daytime time Maximum login time per day.
+.It "expireperiod time Time for expiry allocation.
+.It "graceexpire time Grace days for expired account.
+.It "gracetime time Additional grace login time allowed.
+.It "host.accounted list List of remote host wildcards from which
+login sessions will be accounted.
+.It "host.exempt list List of remote host wildcards from which
+login session accounting is exempted.
+.It "idletime time Maximum idle time before logout.
+.It "minpasswordlen number 6 The minimum length a local
+password may be.
+.It "mixpasswordcase bool true Whether
+.Xr passwd 1
+will warn the user if an all lower case password is entered.
+.It "monthtime time Maximum login time per month.
+.It "passwordtime time Used by
+.Xr passwd 1
+to set next password expiry date.
+.It "refreshtime time New time allowed on account refresh.
+.It "refreshperiod str How often account time is refreshed.
+.It "sessiontime time Maximum login time per session.
+.It "sessionlimit number Maximum number of concurrent
+login sessions on ttys in any group.
+.It "ttys.accounted list List of ttys and ttygroups for which
+login accounting is active.
+.It "ttys.exempt list List of ttys and ttygroups for which login accounting
+is exempt.
+.It "warntime time Advance notice for pending out-of-time.
+.It "weektime time Maximum login time per week.
+.El
+.Pp
+The
+.Em ttys.accounted
+and
+.Em ttys.exempt
+fields operate in a similar manner to
+.Em ttys.allow
+and
+.Em ttys.deny
+as explained
+above.
+Similarly with the
+.Em host.accounted
+and
+.Em host.exempt
+lists.
+.Sh SEE ALSO
+.Xr cap_mkdb 1 ,
+.Xr login 1 ,
+.Xr chroot 2 ,
+.Xr getcap 3 ,
+.Xr getttyent 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr pam 3 ,
+.Xr passwd 5 ,
+.Xr ttys 5 ,
+.Xr ftpd 8 ,
+.Xr pam_passwdqc 8
diff --git a/lib/libutil/login_auth.3 b/lib/libutil/login_auth.3
new file mode 100644
index 0000000..3750bd6
--- /dev/null
+++ b/lib/libutil/login_auth.3
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 29, 1996
+.Os
+.Dt LOGIN_AUTH 3
+.Sh NAME
+.\" .Nm authenticate
+.\" .Nm auth_script
+.\" .Nm auth_env
+.\" .Nm auth_scan
+.\" .Nm auth_rmfiles
+.Nm auth_checknologin ,
+.Nm auth_cat
+.\" .Nm auth_ttyok
+.\" .Nm auth_hostok
+.\" .Nm auth_timesok
+.Nd "authentication style support library for login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.\" .Ft int
+.\". Fn authenticate "const char *name" "const char *classname" "const char *style" "const char *service"
+.\" .Ft int
+.\" .Fn auth_script "const char * path" ...
+.\" .Ft void
+.\" .Fn auth_env "void"
+.\" .Ft int
+.\" .Fn auth_scan "int ok"
+.\" .Ft void
+.\" .Fn auth_rmfiles "void"
+.Ft void
+.Fn auth_checknologin "login_cap_t *lc"
+.Ft int
+.Fn auth_cat "const char *file"
+.\" .Ft int
+.\" .Fn auth_ttyok "login_cap_t *lc" "const char *tty"
+.\" .Ft int
+.\" .Fn auth_hostok "login_cap_t *lc" "const char *hostname" "char const *ip"
+.\" .Ft int
+.\" .Fn auth_timesok "login_cap_t *lc" "time_t now"
+.Sh DESCRIPTION
+This set of functions support the login class authorisation style interface provided
+by
+.Xr login.conf 5 .
+.\" .Sh RETURN VALUES
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_auth.c b/lib/libutil/login_auth.c
new file mode 100644
index 0000000..ce9a2e4
--- /dev/null
+++ b/lib/libutil/login_auth.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Portions copyright (c) 1995,1997 by
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <limits.h>
+#include <login_cap.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+
+/*
+ * auth_checknologin()
+ * Checks for the existance of a nologin file in the login_cap
+ * capability <lc>. If there isn't one specified, then it checks
+ * to see if this class should just ignore nologin files. Lastly,
+ * it tries to print out the default nologin file, and, if such
+ * exists, it exits.
+ */
+
+void
+auth_checknologin(login_cap_t *lc)
+{
+ const char *file;
+
+ /* Do we ignore a nologin file? */
+ if (login_getcapbool(lc, "ignorenologin", 0))
+ return;
+
+ /* Note that <file> will be "" if there is no nologin capability */
+ if ((file = login_getcapstr(lc, "nologin", "", NULL)) == NULL)
+ exit(1);
+
+ /*
+ * *file is true IFF there was a "nologin" capability
+ * Note that auth_cat() returns 1 only if the specified
+ * file exists, and is readable. E.g., /.nologin exists.
+ */
+ if ((*file && auth_cat(file)) || auth_cat(_PATH_NOLOGIN))
+ exit(1);
+}
+
+
+/*
+ * auth_cat()
+ * Checks for the readability of <file>; if it can be opened for
+ * reading, it prints it out to stdout, and then exits. Otherwise,
+ * it returns 0 (meaning no nologin file).
+ */
+
+int
+auth_cat(const char *file)
+{
+ int fd, count;
+ char buf[BUFSIZ];
+
+ if ((fd = open(file, O_RDONLY)) < 0)
+ return 0;
+ while ((count = read(fd, buf, sizeof(buf))) > 0)
+ (void)write(fileno(stdout), buf, count);
+ close(fd);
+ sleep(5); /* wait an arbitrary time to drain */
+ return 1;
+}
diff --git a/lib/libutil/login_cap.3 b/lib/libutil/login_cap.3
new file mode 100644
index 0000000..a533421
--- /dev/null
+++ b/lib/libutil/login_cap.3
@@ -0,0 +1,442 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 27, 1996
+.Os
+.Dt LOGIN_CAP 3
+.Sh NAME
+.Nm login_close ,
+.Nm login_getcapbool ,
+.Nm login_getcaplist ,
+.Nm login_getcapnum ,
+.Nm login_getcapstr ,
+.Nm login_getcapsize ,
+.Nm login_getcaptime ,
+.Nm login_getclass ,
+.Nm login_getclassbyname ,
+.Nm login_getpwclass ,
+.Nm login_getstyle ,
+.Nm login_getuserclass ,
+.Nm login_setcryptfmt
+.Nd "functions for accessing the login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.Ft void
+.Fn login_close "login_cap_t *lc"
+.Ft login_cap_t *
+.Fn login_getclassbyname "const char *nam" "const struct passwd *pwd"
+.Ft login_cap_t *
+.Fn login_getclass "const char *nam"
+.Ft login_cap_t *
+.Fn login_getpwclass "const struct passwd *pwd"
+.Ft login_cap_t *
+.Fn login_getuserclass "const struct passwd *pwd"
+.Ft "const char *"
+.Fn login_getcapstr "login_cap_t *lc" "const char *cap" "const char *def" "const char *error"
+.Ft "const char **"
+.Fn login_getcaplist "login_cap_t *lc" "const char *cap" "const char *chars"
+.Ft "const char *"
+.Fn login_getpath "login_cap_t *lc" "const char *cap" "const char *error"
+.Ft rlim_t
+.Fn login_getcaptime "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft rlim_t
+.Fn login_getcapnum "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft rlim_t
+.Fn login_getcapsize "login_cap_t *lc" "const char *cap" "rlim_t def" "rlim_t error"
+.Ft int
+.Fn login_getcapbool "login_cap_t *lc" "const char *cap" "int def"
+.Ft "const char *"
+.Fn login_getstyle "login_cap_t *lc" "const char *style" "const char *auth"
+.Ft const char *
+.Fn login_setcryptfmt "login_cap_t *lc" "const char *def" "const char *error"
+.Sh DESCRIPTION
+These functions represent a programming interface to the login
+classes database provided in
+.Xr login.conf 5 .
+This database contains capabilities, attributes and default environment
+and accounting settings for users and programs running as specific users,
+as determined by the login class field within entries in
+.Pa /etc/master.passwd .
+.Pp
+Entries in
+.Xr login.conf 5
+consist of colon
+.Ql \&:
+separated fields, the first field in each record being one or more
+identifiers for the record (which must be unique for the entire database),
+each separated by a '|', and may optionally include a description as
+the last 'name'.
+Remaining fields in the record consist of keyword/data pairs.
+Long lines may be continued with a backslash within empty entries,
+with the second and subsequent lines optionally indented for readability.
+This is similar to the format used in
+.Xr termcap 5 ,
+except that keywords are not limited to two significant characters,
+and are usually longer for improved readability.
+As with termcap entries, multiple records can be linked together
+(one record including another) using a field containing tc=<recordid>.
+The result is that the entire record referenced by <recordid> replaces
+the tc= field at the point at which it occurs.
+See
+.Xr getcap 3
+for further details on the format and use of a capabilities database.
+.Pp
+The
+.Nm login_cap
+interface provides a convenient means of retrieving login class
+records with all tc= references expanded.
+A program will typically call one of
+.Fn login_getclass ,
+.Fn login_getpwclass ,
+.Fn login_getuserclass
+or
+.Fn login_getclassbyname
+according to its requirements.
+Each of these functions returns a login capabilities structure,
+.Ft login_cap_t ,
+which may subsequently be used to interrogate the database for
+specific values using the rest of the API.
+Once the login_cap_t is of no further use, the
+.Fn login_close
+function should be called to free all resources used.
+.Pp
+The structure of login_cap_t is defined in login_cap.h, as:
+.Bd -literal -offset indent
+typedef struct {
+ char *lc_class;
+ char *lc_cap;
+ char *lc_style;
+} login_cap_t;
+.Ed
+.Pp
+The
+.Ar lc_class
+member contains a pointer to the name of the login class
+retrieved.
+This may not necessarily be the same as the one requested,
+either directly via
+.Fn login_getclassbyname ,
+indirectly via a user's login record using
+.Fn login_getpwclass ,
+by class name using
+.Fn login_getclass ,
+or
+.Fn login_getuserclass .
+If the referenced user has no login class specified in
+.Pa /etc/master.passwd ,
+the class name is NULL or an empty string.
+If the class
+specified does not exist in the database, each of these
+functions will search for a record with an id of "default",
+with that name returned in the
+.Ar lc_class
+field.
+In addition, if the referenced user has a UID of 0 (normally,
+"root", although the user name is not considered) then
+.Fn login_getpwclass
+will search for a record with an id of "root" before it searches
+for the record with the id of "default".
+.Pp
+The
+.Ar lc_cap
+field is used internally by the library to contain the
+expanded login capabilities record.
+Programs with unusual requirements may wish to use this
+with the lower-level
+.Fn getcap
+style functions to access the record directly.
+.Pp
+The
+.Ar lc_style
+field is set by the
+.Fn login_getstyle
+function to the authorisation style, according to the requirements
+of the program handling a login itself.
+.Pp
+As noted above, the
+.Fn login_get*class
+functions return a login_cap_t object which is used to access
+the matching or default record in the capabilities database.
+The
+.Fn login_getclassbyname
+function accepts two arguments: the first one is the record identifier of the
+record to be retrieved, the second is an optional pointer to a
+.Li passwd
+structure.
+If the first
+.Ar name
+argument is NULL, an empty string, or a class that does not exist
+in the supplemental or system login class database, then the system
+.Em default
+record is returned instead.
+If the second
+.Ar pwd
+parameter is NULL, then only the system login class database is
+used.
+However,
+if the
+.Ar pwd
+parameter and the value of
+.Ar pwd->pw_dir
+are both not NULL, then the directory contained in
+.Ar pwd->pw_dir
+is searched for
+a login database file called ".login_conf", and capability records
+contained within it may override the system defaults.
+This scheme allows users to override some login settings from
+those in the system login class database by creating class records
+for their own private class with a record id of `me'.
+In the context of a
+.Em login ,
+it should be noted that some options cannot by overridden by
+users for two reasons; many options, such as resource settings
+and default process priorities, require root privileges
+in order to take effect, and other fields in the user's file are
+not be consulted at all during the early phases of login for
+security or administrative reasons.
+See
+.Xr login.conf 5
+for more information on which settings a user is able to override.
+Typically, these are limited purely to the user's default login
+environment which might otherwise have been overridden in shell
+startup scripts in any case.
+The user's
+.Pa .login_conf
+merely provides a convenient way for a user to set up their preferred
+login environment before the shell is invoked on login.
+Note that access to the
+.Pa /etc/login.conf
+and
+.Pa .login_conf
+files will only be performed subject to the security checks documented in
+.Xr _secure_path 3
+for the uids 0 and
+.Ar pwd->pw_uid
+respectively.
+.Pp
+If the specified record is NULL, empty or does not exist, and the
+system has no "default" record available to fall back to, there is a
+memory allocation error or for some reason
+.Xr cgetent 3
+is unable to access the login capabilities database, this function
+returns NULL.
+.Pp
+The functions
+.Fn login_getpwclass ,
+.Fn login_getclass
+and
+.Fn login_getuserclass
+retrieve the applicable login class record for the user's passwd
+entry or class name by calling
+.Fn login_getclassbyname .
+On failure, NULL is returned.
+The difference between these functions is that
+.Fn login_getuserclass
+includes the user's overriding
+.Pa .login_conf
+that exists in the user's home directory, and
+.Fn login_getpwclass
+and
+.Fn login_getclass
+restrict lookup only to the system login class database in
+.Pa /etc/login.conf .
+As explained earlier,
+.Fn login_getpwclass
+only differs from
+.Fn login_getclass
+in that it allows the default class for user 'root' as "root"
+if none has been specified in the password database.
+Otherwise, if the passwd pointer is NULL, or the user record
+has no login class, then the system "default" entry is retrieved.
+.Pp
+Once a program no longer wishes to use a login_cap_t object,
+.Fn login_close
+may be called to free all resources used by the login class.
+The
+.Fn login_close
+function may be passed a NULL pointer with no harmful side-effects.
+.Pp
+The remaining functions may be used to retrieve individual
+capability records.
+Each function takes a login_cap_t object as its first parameter,
+a capability tag as the second, and remaining parameters being
+default and error values that are returned if the capability is
+not found.
+The type of the additional parameters passed and returned depend
+on the
+.Em type
+of capability each deals with, be it a simple string, a list,
+a time value, a file or memory size value, a path (consisting of
+a colon-separated list of directories) or a boolean flag.
+The manpage for
+.Xr login.conf 5
+deals in specific tags and their type.
+.Pp
+Note that with all functions in this group, you should not call
+.Xr free 3
+on any pointers returned.
+Memory allocated during retrieval or processing of capability
+tags is automatically reused by subsequent calls to functions
+in this group, or deallocated on calling
+.Fn login_close .
+.Bl -tag -width "login_getcaplist()"
+.It Fn login_getcapstr
+This function returns a simple string capability.
+If the string is not found, then the value in
+.Ar def
+is returned as the default value, or if an error
+occurs, the value in the
+.Ar error
+parameter is returned.
+.It Fn login_getcaplist
+This function returns the value corresponding to the named
+capability tag as a list of values in a NULL terminated
+array.
+Within the login class database, some tags are of type
+.Em list ,
+which consist of one or more comma- or space separated
+values.
+Usually, this function is not called directly from an
+application, but is used indirectly via
+.Fn login_getstyle .
+.It Fn login_getpath
+This function returns a list of directories separated by colons
+.Ql \&: .
+Capability tags for which this function is called consist of a list of
+directories separated by spaces.
+.It Fn login_getcaptime
+This function returns a
+.Em time value
+associated with a particular capability tag with the value expressed
+in seconds (the default), minutes, hours, days, weeks or (365 day)
+years or any combination of these.
+A suffix determines the units used: S for seconds, M for minutes,
+H for hours, D for days, W for weeks and Y for 365 day years.
+Case of the units suffix is ignored.
+.Pp
+Time values are normally used for setting resource, accounting and
+session limits.
+If supported by the operating system and compiler (which is true of
+.Fx ) ,
+the value returned is a quad (long long), of type
+.Em rlim_t .
+A value "inf" or "infinity" may be used to express an infinite
+value, in which case RLIM_INFINITY is returned.
+.It Fn login_getcapnum
+This function returns a numeric value for a tag, expressed either as
+tag=<value> or the standard
+.Fn cgetnum
+format tag#<value>.
+The first format should be used in preference to the second, the
+second format is provided for compatibility and consistency with the
+.Xr getcap 3
+database format where numeric types use the
+.Ql \&#
+as the delimiter for numeric values.
+If in the first format, then the value given may be "inf" or
+"infinity" which results in a return value of RLIM_INFINITY.
+If the given capability tag cannot be found, the
+.Ar def
+parameter is returned, and if an error occurs, the
+.Ar error
+parameter is returned.
+.It Fn login_getcapsize
+.Fn login_getcapsize
+returns a value representing a size (typically, file or memory)
+which may be expressed as bytes (the default), 512 byte blocks,
+kilobytes, megabytes, gigabytes, and on systems that support the
+.Ar long long
+type, terabytes.
+The suffix used determines the units, and multiple values and
+units may be used in combination (e.g.\& 1m500k = 1.5 megabytes).
+A value with no suffix is interpreted as bytes, B as 512-byte
+blocks, K as kilobytes, M as megabytes, G as gigabytes and T as
+terabytes.
+Case is ignored.
+The error value is returned if there is a login capabilities database
+error, if an invalid suffix is used, or if a numeric value cannot be
+interpreted.
+.It Fn login_getcapbool
+This function returns a boolean value tied to a particular flag.
+It returns 0 if the given capability tag is not present or is
+negated by the presence of a "tag@" (See
+.Xr getcap 3
+for more information on boolean flags), and returns 1 if the tag
+is found.
+.It Fn login_getstyle
+This function is used by the login authorisation system to determine
+the style of login available in a particular case.
+The function accepts three parameters, the login_cap entry itself and
+two optional parameters, and authorisation type 'auth' and 'style', and
+applies these to determine the authorisation style that best suites
+these rules.
+.Bl -bullet
+.It
+If 'auth' is neither NULL nor an empty string, look for a tag of type
+"auth-<auth>" in the capability record.
+If not present, then look for the default tag "auth=".
+.It
+If no valid authorisation list was found from the previous step, then
+default to "passwd" as the authorisation list.
+.It
+If 'style' is not NULL or empty, look for it in the list of authorisation
+methods found from the previous step.
+If 'style' is NULL or an empty string, then default to "passwd"
+authorisation.
+.It
+If 'style' is found in the chosen list of authorisation methods, then
+return that, otherwise return NULL.
+.El
+.Pp
+This scheme allows the administrator to determine the types of
+authorisation methods accepted by the system, depending on the
+means by which the access occurs.
+For example, the administrator may require skey or kerberos as
+the authentication method used for access to the system via the
+network, and standard methods via direct dialup or console
+logins, significantly reducing the risk of password discovery
+by "snooping" network packets.
+.It Fn login_setcryptfmt
+The
+.Fn login_setcryptfmt
+function is used to set the
+.Xr crypt 3
+format using the
+.Ql passwd_format
+configuration entry.
+If no entry is found,
+.Fa def
+is taken to be used as the fallback.
+If calling
+.Xr crypt_set_format 3
+on the specifier fails,
+.Fa error
+is returned to indicate this.
+.El
+.Sh SEE ALSO
+.Xr crypt 3 ,
+.Xr getcap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c
new file mode 100644
index 0000000..6d965e3
--- /dev/null
+++ b/lib/libutil/login_cap.c
@@ -0,0 +1,808 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Portions copyright (c) 1995,1997
+ * Berkeley Software Design, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <login_cap.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+/*
+ * allocstr()
+ * Manage a single static pointer for handling a local char* buffer,
+ * resizing as necessary to contain the string.
+ *
+ * allocarray()
+ * Manage a static array for handling a group of strings, resizing
+ * when necessary.
+ */
+
+static int lc_object_count = 0;
+
+static size_t internal_stringsz = 0;
+static char * internal_string = NULL;
+static size_t internal_arraysz = 0;
+static const char ** internal_array = NULL;
+
+static char *
+allocstr(const char *str)
+{
+ char *p;
+
+ size_t sz = strlen(str) + 1; /* realloc() only if necessary */
+ if (sz <= internal_stringsz)
+ p = strcpy(internal_string, str);
+ else if ((p = realloc(internal_string, sz)) != NULL) {
+ internal_stringsz = sz;
+ internal_string = strcpy(p, str);
+ }
+ return p;
+}
+
+
+static const char **
+allocarray(size_t sz)
+{
+ static const char **p;
+
+ if (sz <= internal_arraysz)
+ p = internal_array;
+ else if ((p = realloc(internal_array, sz * sizeof(char*))) != NULL) {
+ internal_arraysz = sz;
+ internal_array = p;
+ }
+ return p;
+}
+
+
+/*
+ * arrayize()
+ * Turn a simple string <str> separated by any of
+ * the set of <chars> into an array. The last element
+ * of the array will be NULL, as is proper.
+ * Free using freearraystr()
+ */
+
+static const char **
+arrayize(const char *str, const char *chars, int *size)
+{
+ int i;
+ char *ptr;
+ const char *cptr;
+ const char **res = NULL;
+
+ /* count the sub-strings */
+ for (i = 0, cptr = str; *cptr; i++) {
+ int count = strcspn(cptr, chars);
+ cptr += count;
+ if (*cptr)
+ ++cptr;
+ }
+
+ /* alloc the array */
+ if ((ptr = allocstr(str)) != NULL) {
+ if ((res = allocarray(++i)) == NULL)
+ free((void *)(uintptr_t)(const void *)str);
+ else {
+ /* now split the string */
+ i = 0;
+ while (*ptr) {
+ int count = strcspn(ptr, chars);
+ res[i++] = ptr;
+ ptr += count;
+ if (*ptr)
+ *ptr++ = '\0';
+ }
+ res[i] = NULL;
+ }
+ }
+
+ if (size)
+ *size = i;
+
+ return res;
+}
+
+
+/*
+ * login_close()
+ * Frees up all resources relating to a login class
+ *
+ */
+
+void
+login_close(login_cap_t * lc)
+{
+ if (lc) {
+ free(lc->lc_style);
+ free(lc->lc_class);
+ free(lc->lc_cap);
+ free(lc);
+ if (--lc_object_count == 0) {
+ free(internal_string);
+ free(internal_array);
+ internal_array = NULL;
+ internal_arraysz = 0;
+ internal_string = NULL;
+ internal_stringsz = 0;
+ cgetclose();
+ }
+ }
+}
+
+
+/*
+ * login_getclassbyname() get the login class by its name.
+ * If the name given is NULL or empty, the default class
+ * LOGIN_DEFCLASS (ie. "default") is fetched. If the
+ * 'pwd' argument is non-NULL and contains an non-NULL
+ * dir entry, then the file _FILE_LOGIN_CONF is picked
+ * up from that directory and used before the system
+ * login database.
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getclassbyname(char const *name, const struct passwd *pwd)
+{
+ login_cap_t *lc;
+
+ if ((lc = malloc(sizeof(login_cap_t))) != NULL) {
+ int r, me, i = 0;
+ uid_t euid = 0;
+ gid_t egid = 0;
+ const char *msg = NULL;
+ const char *dir;
+ char userpath[MAXPATHLEN];
+
+ static char *login_dbarray[] = { NULL, NULL, NULL };
+
+ me = (name != NULL && strcmp(name, LOGIN_MECLASS) == 0);
+ dir = (!me || pwd == NULL) ? NULL : pwd->pw_dir;
+ /*
+ * Switch to user mode before checking/reading its ~/.login_conf
+ * - some NFSes have root read access disabled.
+ *
+ * XXX: This fails to configure additional groups.
+ */
+ if (dir) {
+ euid = geteuid();
+ egid = getegid();
+ (void)setegid(pwd->pw_gid);
+ (void)seteuid(pwd->pw_uid);
+ }
+
+ if (dir && snprintf(userpath, MAXPATHLEN, "%s/%s", dir,
+ _FILE_LOGIN_CONF) < MAXPATHLEN) {
+ login_dbarray[i] = userpath;
+ if (_secure_path(userpath, pwd->pw_uid, pwd->pw_gid) != -1)
+ i++; /* only use 'secure' data */
+ }
+ if (_secure_path(_PATH_LOGIN_CONF, 0, 0) != -1)
+ login_dbarray[i++] = _PATH_LOGIN_CONF;
+ login_dbarray[i] = NULL;
+
+ memset(lc, 0, sizeof(login_cap_t));
+ lc->lc_cap = lc->lc_class = lc->lc_style = NULL;
+
+ if (name == NULL || *name == '\0')
+ name = LOGIN_DEFCLASS;
+
+ switch (cgetent(&lc->lc_cap, login_dbarray, name)) {
+ case -1: /* Failed, entry does not exist */
+ if (me)
+ break; /* Don't retry default on 'me' */
+ if (i == 0)
+ r = -1;
+ else if ((r = open(login_dbarray[0], O_RDONLY)) >= 0)
+ close(r);
+ /*
+ * If there's at least one login class database,
+ * and we aren't searching for a default class
+ * then complain about a non-existent class.
+ */
+ if (r >= 0 || strcmp(name, LOGIN_DEFCLASS) != 0)
+ syslog(LOG_ERR, "login_getclass: unknown class '%s'", name);
+ /* fall-back to default class */
+ name = LOGIN_DEFCLASS;
+ msg = "%s: no default/fallback class '%s'";
+ if (cgetent(&lc->lc_cap, login_dbarray, name) != 0 && r >= 0)
+ break;
+ /* FALLTHROUGH - just return system defaults */
+ case 0: /* success! */
+ if ((lc->lc_class = strdup(name)) != NULL) {
+ if (dir) {
+ (void)seteuid(euid);
+ (void)setegid(egid);
+ }
+ ++lc_object_count;
+ return lc;
+ }
+ msg = "%s: strdup: %m";
+ break;
+ case -2:
+ msg = "%s: retrieving class information: %m";
+ break;
+ case -3:
+ msg = "%s: 'tc=' reference loop '%s'";
+ break;
+ case 1:
+ msg = "couldn't resolve 'tc=' reference in '%s'";
+ break;
+ default:
+ msg = "%s: unexpected cgetent() error '%s': %m";
+ break;
+ }
+ if (dir) {
+ (void)seteuid(euid);
+ (void)setegid(egid);
+ }
+ if (msg != NULL)
+ syslog(LOG_ERR, msg, "login_getclass", name);
+ free(lc);
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ * login_getclass()
+ * Get the login class for the system (only) login class database.
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getclass(const char *cls)
+{
+ return login_getclassbyname(cls, NULL);
+}
+
+
+/*
+ * login_getclass()
+ * Get the login class for a given password entry from
+ * the system (only) login class database.
+ * If the password entry's class field is not set, or
+ * the class specified does not exist, then use the
+ * default of LOGIN_DEFCLASS (ie. "default").
+ * Return a filled-out login_cap_t structure, including
+ * class name, and the capability record buffer.
+ */
+
+login_cap_t *
+login_getpwclass(const struct passwd *pwd)
+{
+ const char *cls = NULL;
+
+ if (pwd != NULL) {
+ cls = pwd->pw_class;
+ if (cls == NULL || *cls == '\0')
+ cls = (pwd->pw_uid == 0) ? LOGIN_DEFROOTCLASS : LOGIN_DEFCLASS;
+ }
+ return login_getclassbyname(cls, pwd);
+}
+
+
+/*
+ * login_getuserclass()
+ * Get the login class for a given password entry, allowing user
+ * overrides via ~/.login_conf.
+ */
+
+login_cap_t *
+login_getuserclass(const struct passwd *pwd)
+{
+ return login_getclassbyname(LOGIN_MECLASS, pwd);
+}
+
+
+
+/*
+ * login_getcapstr()
+ * Given a login_cap entry, and a capability name, return the
+ * value defined for that capability, a default if not found, or
+ * an error string on error.
+ */
+
+const char *
+login_getcapstr(login_cap_t *lc, const char *cap, const char *def, const char *error)
+{
+ char *res;
+ int ret;
+
+ if (lc == NULL || cap == NULL || lc->lc_cap == NULL || *cap == '\0')
+ return def;
+
+ if ((ret = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ return (ret >= 0) ? res : error;
+}
+
+
+/*
+ * login_getcaplist()
+ * Given a login_cap entry, and a capability name, return the
+ * value defined for that capability split into an array of
+ * strings.
+ */
+
+const char **
+login_getcaplist(login_cap_t *lc, const char *cap, const char *chars)
+{
+ const char *lstring;
+
+ if (chars == NULL)
+ chars = ", \t";
+ if ((lstring = login_getcapstr(lc, cap, NULL, NULL)) != NULL)
+ return arrayize(lstring, chars, NULL);
+ return NULL;
+}
+
+
+/*
+ * login_getpath()
+ * From the login_cap_t <lc>, get the capability <cap> which is
+ * formatted as either a space or comma delimited list of paths
+ * and append them all into a string and separate by semicolons.
+ * If there is an error of any kind, return <error>.
+ */
+
+const char *
+login_getpath(login_cap_t *lc, const char *cap, const char *error)
+{
+ const char *str;
+ char *ptr;
+ int count;
+
+ str = login_getcapstr(lc, cap, NULL, NULL);
+ if (str == NULL)
+ return error;
+ ptr = __DECONST(char *, str); /* XXXX Yes, very dodgy */
+ while (*ptr) {
+ count = strcspn(ptr, ", \t");
+ ptr += count;
+ if (*ptr)
+ *ptr++ = ':';
+ }
+ return str;
+}
+
+
+static int
+isinfinite(const char *s)
+{
+ static const char *infs[] = {
+ "infinity",
+ "inf",
+ "unlimited",
+ "unlimit",
+ "-1",
+ NULL
+ };
+ const char **i = &infs[0];
+
+ while (*i != NULL) {
+ if (strcasecmp(s, *i) == 0)
+ return 1;
+ ++i;
+ }
+ return 0;
+}
+
+
+static u_quad_t
+rmultiply(u_quad_t n1, u_quad_t n2)
+{
+ u_quad_t m, r;
+ int b1, b2;
+
+ static int bpw = 0;
+
+ /* Handle simple cases */
+ if (n1 == 0 || n2 == 0)
+ return 0;
+ if (n1 == 1)
+ return n2;
+ if (n2 == 1)
+ return n1;
+
+ /*
+ * sizeof() returns number of bytes needed for storage.
+ * This may be different from the actual number of useful bits.
+ */
+ if (!bpw) {
+ bpw = sizeof(u_quad_t) * 8;
+ while (((u_quad_t)1 << (bpw-1)) == 0)
+ --bpw;
+ }
+
+ /*
+ * First check the magnitude of each number. If the sum of the
+ * magnatude is way to high, reject the number. (If this test
+ * is not done then the first multiply below may overflow.)
+ */
+ for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1)
+ ;
+ for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2)
+ ;
+ if (b1 + b2 - 2 > bpw) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+
+ /*
+ * Decompose the multiplication to be:
+ * h1 = n1 & ~1
+ * h2 = n2 & ~1
+ * l1 = n1 & 1
+ * l2 = n2 & 1
+ * (h1 + l1) * (h2 + l2)
+ * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2)
+ *
+ * Since h1 && h2 do not have the low bit set, we can then say:
+ *
+ * (h1>>1 * h2>>1 * 4) + ...
+ *
+ * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will
+ * overflow.
+ *
+ * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2)
+ * then adding in residual amout will cause an overflow.
+ */
+
+ m = (n1 >> 1) * (n2 >> 1);
+ if (m >= ((u_quad_t)1 << (bpw-2))) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+ m *= 4;
+
+ r = (n1 & n2 & 1)
+ + (n2 & 1) * (n1 & ~(u_quad_t)1)
+ + (n1 & 1) * (n2 & ~(u_quad_t)1);
+
+ if ((u_quad_t)(m + r) < m) {
+ errno = ERANGE;
+ return (UQUAD_MAX);
+ }
+ m += r;
+
+ return (m);
+}
+
+
+/*
+ * login_getcaptime()
+ * From the login_cap_t <lc>, get the capability <cap>, which is
+ * formatted as a time (e.g., "<cap>=10h3m2s"). If <cap> is not
+ * present in <lc>, return <def>; if there is an error of some kind,
+ * return <error>.
+ */
+
+rlim_t
+login_getcaptime(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *res, *ep, *oval;
+ int r;
+ rlim_t tot;
+
+ errno = 0;
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ /*
+ * Look for <cap> in lc_cap.
+ * If it's not there (-1), return <def>.
+ * If there's an error, return <error>.
+ */
+
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ else if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ /* "inf" and "infinity" are special cases */
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ /*
+ * Now go through the string, turning something like 1h2m3s into
+ * an integral value. Whee.
+ */
+
+ errno = 0;
+ tot = 0;
+ oval = res;
+ while (*res) {
+ rlim_t tim = strtoq(res, &ep, 0);
+ rlim_t mult = 1;
+
+ if (ep == NULL || ep == res || errno != 0) {
+ invalid:
+ syslog(LOG_WARNING, "login_getcaptime: class '%s' bad value %s=%s",
+ lc->lc_class, cap, oval);
+ errno = ERANGE;
+ return error;
+ }
+ /* Look for suffixes */
+ switch (*ep++) {
+ case 0:
+ ep--;
+ break; /* end of string */
+ case 's': case 'S': /* seconds */
+ break;
+ case 'm': case 'M': /* minutes */
+ mult = 60;
+ break;
+ case 'h': case 'H': /* hours */
+ mult = 60L * 60L;
+ break;
+ case 'd': case 'D': /* days */
+ mult = 60L * 60L * 24L;
+ break;
+ case 'w': case 'W': /* weeks */
+ mult = 60L * 60L * 24L * 7L;
+ break;
+ case 'y': case 'Y': /* 365-day years */
+ mult = 60L * 60L * 24L * 365L;
+ break;
+ default:
+ goto invalid;
+ }
+ res = ep;
+ tot += rmultiply(tim, mult);
+ if (errno)
+ goto invalid;
+ }
+
+ return tot;
+}
+
+
+/*
+ * login_getcapnum()
+ * From the login_cap_t <lc>, extract the numerical value <cap>.
+ * If it is not present, return <def> for a default, and return
+ * <error> if there is an error.
+ * Like login_getcaptime(), only it only converts to a number, not
+ * to a time; "infinity" and "inf" are 'special.'
+ */
+
+rlim_t
+login_getcapnum(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *ep, *res;
+ int r;
+ rlim_t val;
+
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ /*
+ * For BSDI compatibility, try for the tag=<val> first
+ */
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1) {
+ long lval;
+ /* string capability not present, so try for tag#<val> as numeric */
+ if ((r = cgetnum(lc->lc_cap, cap, &lval)) == -1)
+ return def; /* Not there, so return default */
+ else if (r >= 0)
+ return (rlim_t)lval;
+ }
+
+ if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ errno = 0;
+ val = strtoq(res, &ep, 0);
+ if (ep == NULL || ep == res || errno != 0) {
+ syslog(LOG_WARNING, "login_getcapnum: class '%s' bad value %s=%s",
+ lc->lc_class, cap, res);
+ errno = ERANGE;
+ return error;
+ }
+
+ return val;
+}
+
+
+
+/*
+ * login_getcapsize()
+ * From the login_cap_t <lc>, extract the capability <cap>, which is
+ * formatted as a size (e.g., "<cap>=10M"); it can also be "infinity".
+ * If not present, return <def>, or <error> if there is an error of
+ * some sort.
+ */
+
+rlim_t
+login_getcapsize(login_cap_t *lc, const char *cap, rlim_t def, rlim_t error)
+{
+ char *ep, *res, *oval;
+ int r;
+ rlim_t tot;
+
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+
+ if ((r = cgetstr(lc->lc_cap, cap, &res)) == -1)
+ return def;
+ else if (r < 0) {
+ errno = ERANGE;
+ return error;
+ }
+
+ if (isinfinite(res))
+ return RLIM_INFINITY;
+
+ errno = 0;
+ tot = 0;
+ oval = res;
+ while (*res) {
+ rlim_t siz = strtoq(res, &ep, 0);
+ rlim_t mult = 1;
+
+ if (ep == NULL || ep == res || errno != 0) {
+ invalid:
+ syslog(LOG_WARNING, "login_getcapsize: class '%s' bad value %s=%s",
+ lc->lc_class, cap, oval);
+ errno = ERANGE;
+ return error;
+ }
+ switch (*ep++) {
+ case 0: /* end of string */
+ ep--;
+ break;
+ case 'b': case 'B': /* 512-byte blocks */
+ mult = 512;
+ break;
+ case 'k': case 'K': /* 1024-byte Kilobytes */
+ mult = 1024;
+ break;
+ case 'm': case 'M': /* 1024-k kbytes */
+ mult = 1024 * 1024;
+ break;
+ case 'g': case 'G': /* 1Gbyte */
+ mult = 1024 * 1024 * 1024;
+ break;
+ case 't': case 'T': /* 1TBte */
+ mult = 1024LL * 1024LL * 1024LL * 1024LL;
+ break;
+ default:
+ goto invalid;
+ }
+ res = ep;
+ tot += rmultiply(siz, mult);
+ if (errno)
+ goto invalid;
+ }
+
+ return tot;
+}
+
+
+/*
+ * login_getcapbool()
+ * From the login_cap_t <lc>, check for the existance of the capability
+ * of <cap>. Return <def> if <lc>->lc_cap is NULL, otherwise return
+ * the whether or not <cap> exists there.
+ */
+
+int
+login_getcapbool(login_cap_t *lc, const char *cap, int def)
+{
+ if (lc == NULL || lc->lc_cap == NULL)
+ return def;
+ return (cgetcap(lc->lc_cap, cap, ':') != NULL);
+}
+
+
+/*
+ * login_getstyle()
+ * Given a login_cap entry <lc>, and optionally a type of auth <auth>,
+ * and optionally a style <style>, find the style that best suits these
+ * rules:
+ * 1. If <auth> is non-null, look for an "auth-<auth>=" string
+ * in the capability; if not present, default to "auth=".
+ * 2. If there is no auth list found from (1), default to
+ * "passwd" as an authorization list.
+ * 3. If <style> is non-null, look for <style> in the list of
+ * authorization methods found from (2); if <style> is NULL, default
+ * to LOGIN_DEFSTYLE ("passwd").
+ * 4. If the chosen style is found in the chosen list of authorization
+ * methods, return that; otherwise, return NULL.
+ * E.g.:
+ * login_getstyle(lc, NULL, "ftp");
+ * login_getstyle(lc, "login", NULL);
+ * login_getstyle(lc, "skey", "network");
+ */
+
+const char *
+login_getstyle(login_cap_t *lc, const char *style, const char *auth)
+{
+ int i;
+ const char **authtypes = NULL;
+ char *auths= NULL;
+ char realauth[64];
+
+ static const char *defauthtypes[] = { LOGIN_DEFSTYLE, NULL };
+
+ if (auth != NULL && *auth != '\0') {
+ if (snprintf(realauth, sizeof realauth, "auth-%s", auth) < (int)sizeof(realauth))
+ authtypes = login_getcaplist(lc, realauth, NULL);
+ }
+
+ if (authtypes == NULL)
+ authtypes = login_getcaplist(lc, "auth", NULL);
+
+ if (authtypes == NULL)
+ authtypes = defauthtypes;
+
+ /*
+ * We have at least one authtype now; auths is a comma-separated
+ * (or space-separated) list of authentication types. We have to
+ * convert from this to an array of char*'s; authtypes then gets this.
+ */
+ i = 0;
+ if (style != NULL && *style != '\0') {
+ while (authtypes[i] != NULL && strcmp(style, authtypes[i]) != 0)
+ i++;
+ }
+
+ lc->lc_style = NULL;
+ if (authtypes[i] != NULL && (auths = strdup(authtypes[i])) != NULL)
+ lc->lc_style = auths;
+
+ if (lc->lc_style != NULL)
+ lc->lc_style = strdup(lc->lc_style);
+
+ return lc->lc_style;
+}
diff --git a/lib/libutil/login_cap.h b/lib/libutil/login_cap.h
new file mode 100644
index 0000000..a7cb4c6
--- /dev/null
+++ b/lib/libutil/login_cap.h
@@ -0,0 +1,158 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Low-level routines relating to the user capabilities database
+ *
+ * Was login_cap.h,v 1.9 1997/05/07 20:00:01 eivind Exp
+ * $FreeBSD$
+ */
+
+#ifndef _LOGIN_CAP_H_
+#define _LOGIN_CAP_H_
+
+#define LOGIN_DEFCLASS "default"
+#define LOGIN_DEFROOTCLASS "root"
+#define LOGIN_MECLASS "me"
+#define LOGIN_DEFSTYLE "passwd"
+#define LOGIN_DEFSERVICE "login"
+#define LOGIN_DEFUMASK 022
+#define LOGIN_DEFPRI 0
+#define _PATH_LOGIN_CONF "/etc/login.conf"
+#define _FILE_LOGIN_CONF ".login_conf"
+#define _PATH_AUTHPROG "/usr/libexec/login_"
+
+#define LOGIN_SETGROUP 0x0001 /* set group */
+#define LOGIN_SETLOGIN 0x0002 /* set login (via setlogin) */
+#define LOGIN_SETPATH 0x0004 /* set path */
+#define LOGIN_SETPRIORITY 0x0008 /* set priority */
+#define LOGIN_SETRESOURCES 0x0010 /* set resources (cputime, etc.) */
+#define LOGIN_SETUMASK 0x0020 /* set umask, obviously */
+#define LOGIN_SETUSER 0x0040 /* set user (via setuid) */
+#define LOGIN_SETENV 0x0080 /* set user environment */
+#define LOGIN_SETMAC 0x0100 /* set user default MAC label */
+#define LOGIN_SETALL 0x01ff /* set everything */
+
+#define BI_AUTH "authorize" /* accepted authentication */
+#define BI_REJECT "reject" /* rejected authentication */
+#define BI_CHALLENG "reject challenge" /* reject with a challenge */
+#define BI_SILENT "reject silent" /* reject silently */
+#define BI_REMOVE "remove" /* remove file on error */
+#define BI_ROOTOKAY "authorize root" /* root authenticated */
+#define BI_SECURE "authorize secure" /* okay on non-secure line */
+#define BI_SETENV "setenv" /* set environment variable */
+#define BI_VALUE "value" /* set local variable */
+
+#define AUTH_OKAY 0x01 /* user authenticated */
+#define AUTH_ROOTOKAY 0x02 /* root login okay */
+#define AUTH_SECURE 0x04 /* secure login */
+#define AUTH_SILENT 0x08 /* silent rejection */
+#define AUTH_CHALLENGE 0x10 /* a chellenge was given */
+
+#define AUTH_ALLOW (AUTH_OKAY | AUTH_ROOTOKAY | AUTH_SECURE)
+
+typedef struct login_cap {
+ char *lc_class;
+ char *lc_cap;
+ char *lc_style;
+} login_cap_t;
+
+typedef struct login_time {
+ u_short lt_start; /* Start time */
+ u_short lt_end; /* End time */
+#define LTM_NONE 0x00
+#define LTM_SUN 0x01
+#define LTM_MON 0x02
+#define LTM_TUE 0x04
+#define LTM_WED 0x08
+#define LTM_THU 0x10
+#define LTM_FRI 0x20
+#define LTM_SAT 0x40
+#define LTM_ANY 0x7F
+#define LTM_WK 0x3E
+#define LTM_WD 0x41
+ u_char lt_dow; /* Days of week */
+} login_time_t;
+
+#define LC_MAXTIMES 64
+
+#include <sys/cdefs.h>
+__BEGIN_DECLS
+struct passwd;
+
+void login_close(login_cap_t *);
+login_cap_t *login_getclassbyname(const char *, const struct passwd *);
+login_cap_t *login_getclass(const char *);
+login_cap_t *login_getpwclass(const struct passwd *);
+login_cap_t *login_getuserclass(const struct passwd *);
+
+const char *login_getcapstr(login_cap_t*, const char *, const char *, const char *);
+const char **login_getcaplist(login_cap_t *, const char *, const char *);
+const char *login_getstyle(login_cap_t *, const char *, const char *);
+rlim_t login_getcaptime(login_cap_t *, const char *, rlim_t, rlim_t);
+rlim_t login_getcapnum(login_cap_t *, const char *, rlim_t, rlim_t);
+rlim_t login_getcapsize(login_cap_t *, const char *, rlim_t, rlim_t);
+const char *login_getpath(login_cap_t *, const char *, const char *);
+int login_getcapbool(login_cap_t *, const char *, int);
+const char *login_setcryptfmt(login_cap_t *, const char *, const char *);
+
+int setclasscontext(const char*, unsigned int);
+int setusercontext(login_cap_t*, const struct passwd*, uid_t, unsigned int);
+void setclassresources(login_cap_t *);
+void setclassenvironment(login_cap_t *, const struct passwd *, int);
+
+/* Most of these functions are deprecated */
+int auth_approve(login_cap_t*, const char*, const char*);
+int auth_check(const char *, const char *, const char *, const char *, int *);
+void auth_env(void);
+char *auth_mkvalue(const char *n);
+int auth_response(const char *, const char *, const char *, const char *, int *, const char *, const char *);
+void auth_rmfiles(void);
+int auth_scan(int);
+int auth_script(const char*, ...);
+int auth_script_data(const char *, int, const char *, ...);
+char *auth_valud(const char *);
+int auth_setopt(const char *, const char *);
+void auth_clropts(void);
+
+void auth_checknologin(login_cap_t*);
+int auth_cat(const char*);
+
+int auth_ttyok(login_cap_t*, const char *);
+int auth_hostok(login_cap_t*, const char *, char const *);
+int auth_timeok(login_cap_t*, time_t);
+
+struct tm;
+
+login_time_t parse_lt(const char *);
+int in_ltm(const login_time_t *, struct tm *, time_t *);
+int in_ltms(const login_time_t *, struct tm *, time_t *);
+
+/* helper functions */
+
+int login_strinlist(const char **, char const *, int);
+int login_str2inlist(const char **, const char *, const char *, int);
+login_time_t * login_timelist(login_cap_t *, char const *, int *, login_time_t **);
+int login_ttyok(login_cap_t *, const char *, const char *, const char *);
+int login_hostok(login_cap_t *, const char *, const char *, const char *, const char *);
+
+__END_DECLS
+
+#endif /* _LOGIN_CAP_H_ */
diff --git a/lib/libutil/login_class.3 b/lib/libutil/login_class.3
new file mode 100644
index 0000000..8f324dd
--- /dev/null
+++ b/lib/libutil/login_class.3
@@ -0,0 +1,197 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 28, 1996
+.Os
+.Dt LOGIN_CLASS 3
+.Sh NAME
+.Nm setclasscontext ,
+.Nm setclassenvironment ,
+.Nm setclassresources ,
+.Nm setusercontext
+.Nd "functions for using the login class capabilities database"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In login_cap.h
+.Ft int
+.Fn setclasscontext "const char *classname" "unsigned int flags"
+.Ft int
+.Fn setusercontext "login_cap_t *lc" "const struct passwd *pwd" "uid_t uid" "unsigned int flags"
+.Ft void
+.Fn setclassresources "login_cap_t *lc"
+.Ft void
+.Fn setclassenvironment "login_cap_t *lc" "const struct passwd *pwd" "int paths"
+.Sh DESCRIPTION
+These functions provide a higher level interface to the login class
+database than those documented in
+.Xr login_cap 3 .
+These functions are used to set resource limits, environment and
+accounting settings for users on logging into the system and when
+selecting an appropriate set of environment and resource settings
+for system daemons based on login classes.
+These functions may only be called if the current process is
+running with root privileges.
+If the LOGIN_SETLOGIN flag is used this function calls
+.Xr setlogin 2 ,
+and due care must be taken as detailed in the manpage for that
+function and this affects all processes running in the same session
+and not just the current process.
+.Pp
+The
+.Fn setclasscontext
+function sets various class context values (resource limits, umask and
+process priorities) based on values for a specific named class.
+.Pp
+The
+.Fn setusercontext
+function sets class context values based on a given login_cap_t
+object and a specific passwd record (if login_cap_t is NULL),
+the current session's login, and the current process
+user and group ownership.
+Each of these actions is selectable via bit-flags passed
+in the
+.Ar flags
+parameter, which is comprised of one or more of the following:
+.Bl -tag -width LOGIN_SETRESOURCES
+.It LOGIN_SETLOGIN
+Set the login associated with the current session to the user
+specified in the passwd structure using
+.Xr setlogin 2 .
+The
+.Ar pwd
+parameter must not be NULL if this option is used.
+.It LOGIN_SETUSER
+Set ownship of the current process to the uid specified in the
+.Ar uid
+parameter using
+.Xr setuid 2 .
+.It LOGIN_SETGROUP
+Set group ownership of the current process to the group id
+specified in the passwd structure using
+.Xr setgid 2 ,
+and calls
+.Xr initgroups 3
+to set up the group access list for the current process.
+The
+.Ar pwd
+parameter must not be NULL if this option is used.
+.It LOGIN_SETRESOURCES
+Set resource limits for the current process based on values
+specified in the system login class database.
+Class capability tags used, with and without -cur (soft limit)
+or -max (hard limit) suffixes and the corresponding resource
+setting:
+.Bd -literal
+cputime RLIMIT_CPU
+filesize RLIMIT_FSIZE
+datasize RLIMIT_DATA
+stacksize RLIMIT_STACK
+coredumpsize RLIMIT_CORE
+memoryuse RLIMIT_RSS
+memorylocked RLIMIT_MEMLOCK
+maxproc RLIMIT_NPROC
+openfiles RLIMIT_NOFILE
+sbsize RLIMIT_SBSIZE
+vmemoryuse RLIMIT_VMEM
+.Ed
+.It LOGIN_SETPRIORITY
+Set the scheduling priority for the current process based on the
+value specified in the system login class database.
+Class capability tags used:
+.Bd -literal
+priority
+.Ed
+.It LOGIN_SETUMASK
+Set the umask for the current process to a value in the user or
+system login class database.
+Class capability tags used:
+.Bd -literal
+umask
+.Ed
+.It LOGIN_SETPATH
+Set the "path" and "manpath" environment variables based on values
+in the user or system login class database.
+Class capability tags used with the corresponding environment
+variables set:
+.Bd -literal
+path PATH
+manpath MANPATH
+.Ed
+.It LOGIN_SETENV
+Set various environment variables based on values in the user or
+system login class database.
+Class capability tags used with the corresponding environment
+variables set:
+.Bd -literal
+lang LANG
+charset MM_CHARSET
+timezone TZ
+term TERM
+.Ed
+.Pp
+Additional environment variables may be set using the list type
+capability "setenv=var1 val1,var2 val2..,varN valN".
+.It LOGIN_SETMAC
+Set the MAC label for the current process to the label specified
+in system login class database.
+.Pp
+.It LOGIN_SETALL
+Enables all of the above settings.
+.El
+.Pp
+Note that when setting environment variables and a valid passwd
+pointer is provided in the
+.Ar pwd
+parameter, the characters
+.Ql \&~
+and
+.Ql \&$
+are substituted for the user's home directory and login name
+respectively.
+.Pp
+The
+.Fn setclassresources
+and
+.Fn setclassenvironment
+functions are subsets of the setcontext functions above, but may
+be useful in isolation.
+.Sh RETURN VALUES
+The
+.Fn setclasscontext
+and
+.Fn setusercontext
+functions return -1 if an error occurred, or 0 on success.
+If an error occurs when attempting to set the user, login, group
+or resources, a message is reported to
+.Xr syslog 3 ,
+with LOG_ERR priority and directed to the currently active facility.
+.Sh SEE ALSO
+.Xr setgid 2 ,
+.Xr setlogin 2 ,
+.Xr setuid 2 ,
+.Xr getcap 3 ,
+.Xr initgroups 3 ,
+.Xr login_cap 3 ,
+.Xr mac_set_proc 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_class.c b/lib/libutil/login_class.c
new file mode 100644
index 0000000..3071605
--- /dev/null
+++ b/lib/libutil/login_class.c
@@ -0,0 +1,429 @@
+/*-
+ * Copyright (c) 1996 by
+ * Sean Eric Fagan <sef@kithrup.com>
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * High-level routines relating to use of the user capabilities database
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mac.h>
+#include <sys/rtprio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <login_cap.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+
+static struct login_res {
+ const char *what;
+ rlim_t (*who)(login_cap_t *, const char *, rlim_t, rlim_t);
+ int why;
+} resources[] = {
+ { "cputime", login_getcaptime, RLIMIT_CPU },
+ { "filesize", login_getcapsize, RLIMIT_FSIZE },
+ { "datasize", login_getcapsize, RLIMIT_DATA },
+ { "stacksize", login_getcapsize, RLIMIT_STACK },
+ { "memoryuse", login_getcapsize, RLIMIT_RSS },
+ { "memorylocked", login_getcapsize, RLIMIT_MEMLOCK },
+ { "maxproc", login_getcapnum, RLIMIT_NPROC },
+ { "openfiles", login_getcapnum, RLIMIT_NOFILE },
+ { "coredumpsize", login_getcapsize, RLIMIT_CORE },
+ { "sbsize", login_getcapsize, RLIMIT_SBSIZE },
+ { "vmemoryuse", login_getcapsize, RLIMIT_VMEM },
+ { NULL, 0, 0 }
+};
+
+
+void
+setclassresources(login_cap_t *lc)
+{
+ struct login_res *lr;
+
+ if (lc == NULL)
+ return;
+
+ for (lr = resources; lr->what != NULL; ++lr) {
+ struct rlimit rlim;
+
+ /*
+ * The login.conf file can have <limit>, <limit>-max, and
+ * <limit>-cur entries.
+ * What we do is get the current current- and maximum- limits.
+ * Then, we try to get an entry for <limit> from the capability,
+ * using the current and max limits we just got as the
+ * default/error values.
+ * *Then*, we try looking for <limit>-cur and <limit>-max,
+ * again using the appropriate values as the default/error
+ * conditions.
+ */
+
+ if (getrlimit(lr->why, &rlim) != 0)
+ syslog(LOG_ERR, "getting %s resource limit: %m", lr->what);
+ else {
+ char name_cur[40];
+ char name_max[40];
+ rlim_t rcur = rlim.rlim_cur;
+ rlim_t rmax = rlim.rlim_max;
+
+ sprintf(name_cur, "%s-cur", lr->what);
+ sprintf(name_max, "%s-max", lr->what);
+
+ rcur = (*lr->who)(lc, lr->what, rcur, rcur);
+ rmax = (*lr->who)(lc, lr->what, rmax, rmax);
+ rlim.rlim_cur = (*lr->who)(lc, name_cur, rcur, rcur);
+ rlim.rlim_max = (*lr->who)(lc, name_max, rmax, rmax);
+
+ if (setrlimit(lr->why, &rlim) == -1)
+ syslog(LOG_WARNING, "set class '%s' resource limit %s: %m", lc->lc_class, lr->what);
+ }
+ }
+}
+
+
+
+static struct login_vars {
+ const char *tag;
+ const char *var;
+ const char *def;
+ int overwrite;
+} pathvars[] = {
+ { "path", "PATH", NULL, 1},
+ { "cdpath", "CDPATH", NULL, 1},
+ { "manpath", "MANPATH", NULL, 1},
+ { NULL, NULL, NULL, 0}
+}, envars[] = {
+ { "lang", "LANG", NULL, 1},
+ { "charset", "MM_CHARSET", NULL, 1},
+ { "timezone", "TZ", NULL, 1},
+ { "term", "TERM", NULL, 0},
+ { NULL, NULL, NULL, 0}
+};
+
+static char *
+substvar(const char * var, const struct passwd * pwd, int hlen, int pch, int nlen)
+{
+ char *np = NULL;
+
+ if (var != NULL) {
+ int tildes = 0;
+ int dollas = 0;
+ char *p;
+
+ if (pwd != NULL) {
+ /* Count the number of ~'s in var to substitute */
+ for (p = (char *)var; (p = strchr(p, '~')) != NULL; p++)
+ ++tildes;
+ /* Count the number of $'s in var to substitute */
+ for (p = (char *)var; (p = strchr(p, '$')) != NULL; p++)
+ ++dollas;
+ }
+
+ np = malloc(strlen(var) + (dollas * nlen)
+ - dollas + (tildes * (pch+hlen))
+ - tildes + 1);
+
+ if (np != NULL) {
+ p = strcpy(np, var);
+
+ if (pwd != NULL) {
+ /*
+ * This loop does user username and homedir substitutions
+ * for unescaped $ (username) and ~ (homedir)
+ */
+ while (*(p += strcspn(p, "~$")) != '\0') {
+ int l = strlen(p);
+
+ if (p > np && *(p-1) == '\\') /* Escaped: */
+ memmove(p - 1, p, l + 1); /* Slide-out the backslash */
+ else if (*p == '~') {
+ int v = pch && *(p+1) != '/'; /* Avoid double // */
+ memmove(p + hlen + v, p + 1, l); /* Subst homedir */
+ memmove(p, pwd->pw_dir, hlen);
+ if (v)
+ p[hlen] = '/';
+ p += hlen + v;
+ }
+ else /* if (*p == '$') */ {
+ memmove(p + nlen, p + 1, l); /* Subst username */
+ memmove(p, pwd->pw_name, nlen);
+ p += nlen;
+ }
+ }
+ }
+ }
+ }
+
+ return np;
+}
+
+
+void
+setclassenvironment(login_cap_t *lc, const struct passwd * pwd, int paths)
+{
+ struct login_vars *vars = paths ? pathvars : envars;
+ int hlen = pwd ? strlen(pwd->pw_dir) : 0;
+ int nlen = pwd ? strlen(pwd->pw_name) : 0;
+ char pch = 0;
+
+ if (hlen && pwd->pw_dir[hlen-1] != '/')
+ ++pch;
+
+ while (vars->tag != NULL) {
+ const char * var = paths ? login_getpath(lc, vars->tag, NULL)
+ : login_getcapstr(lc, vars->tag, NULL, NULL);
+
+ char * np = substvar(var, pwd, hlen, pch, nlen);
+
+ if (np != NULL) {
+ setenv(vars->var, np, vars->overwrite);
+ free(np);
+ } else if (vars->def != NULL) {
+ setenv(vars->var, vars->def, 0);
+ }
+ ++vars;
+ }
+
+ /*
+ * If we're not processing paths, then see if there is a setenv list by
+ * which the admin and/or user may set an arbitrary set of env vars.
+ */
+ if (!paths) {
+ const char **set_env = login_getcaplist(lc, "setenv", ",");
+
+ if (set_env != NULL) {
+ while (*set_env != NULL) {
+ char *p = strchr(*set_env, '=');
+
+ if (p != NULL) { /* Discard invalid entries */
+ char *np;
+
+ *p++ = '\0';
+ if ((np = substvar(p, pwd, hlen, pch, nlen)) != NULL) {
+ setenv(*set_env, np, 1);
+ free(np);
+ }
+ }
+ ++set_env;
+ }
+ }
+ }
+}
+
+
+/*
+ * setclasscontext()
+ *
+ * For the login class <class>, set various class context values
+ * (limits, mainly) to the values for that class. Which values are
+ * set are controlled by <flags> -- see <login_class.h> for the
+ * possible values.
+ *
+ * setclasscontext() can only set resources, priority, and umask.
+ */
+
+int
+setclasscontext(const char *classname, unsigned int flags)
+{
+ int rc;
+ login_cap_t *lc;
+
+ lc = login_getclassbyname(classname, NULL);
+
+ flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY |
+ LOGIN_SETUMASK | LOGIN_SETPATH;
+
+ rc = lc ? setusercontext(lc, NULL, 0, flags) : -1;
+ login_close(lc);
+ return rc;
+}
+
+
+
+/*
+ * Private functionw which takes care of processing
+ */
+
+static mode_t
+setlogincontext(login_cap_t *lc, const struct passwd *pwd,
+ mode_t mymask, unsigned long flags)
+{
+ if (lc) {
+ /* Set resources */
+ if (flags & LOGIN_SETRESOURCES)
+ setclassresources(lc);
+ /* See if there's a umask override */
+ if (flags & LOGIN_SETUMASK)
+ mymask = (mode_t)login_getcapnum(lc, "umask", mymask, mymask);
+ /* Set paths */
+ if (flags & LOGIN_SETPATH)
+ setclassenvironment(lc, pwd, 1);
+ /* Set environment */
+ if (flags & LOGIN_SETENV)
+ setclassenvironment(lc, pwd, 0);
+ }
+ return mymask;
+}
+
+
+
+/*
+ * setusercontext()
+ *
+ * Given a login class <lc> and a user in <pwd>, with a uid <uid>,
+ * set the context as in setclasscontext(). <flags> controls which
+ * values are set.
+ *
+ * The difference between setclasscontext() and setusercontext() is
+ * that the former sets things up for an already-existing process,
+ * while the latter sets things up from a root context. Such as might
+ * be called from login(1).
+ *
+ */
+
+int
+setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags)
+{
+ quad_t p;
+ mode_t mymask;
+ login_cap_t *llc = NULL;
+ struct rtprio rtp;
+ int error;
+
+ if (lc == NULL) {
+ if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL)
+ llc = lc; /* free this when we're done */
+ }
+
+ if (flags & LOGIN_SETPATH)
+ pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH;
+
+ /* we need a passwd entry to set these */
+ if (pwd == NULL)
+ flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC);
+
+ /* Set the process priority */
+ if (flags & LOGIN_SETPRIORITY) {
+ p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI);
+
+ if(p > PRIO_MAX) {
+ rtp.type = RTP_PRIO_IDLE;
+ rtp.prio = p - PRIO_MAX - 1;
+ p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p;
+ if(rtprio(RTP_SET, 0, &rtp))
+ syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
+ pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
+ } else if(p < PRIO_MIN) {
+ rtp.type = RTP_PRIO_REALTIME;
+ rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX);
+ p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p;
+ if(rtprio(RTP_SET, 0, &rtp))
+ syslog(LOG_WARNING, "rtprio '%s' (%s): %m",
+ pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
+ } else {
+ if (setpriority(PRIO_PROCESS, 0, (int)p) != 0)
+ syslog(LOG_WARNING, "setpriority '%s' (%s): %m",
+ pwd->pw_name, lc ? lc->lc_class : LOGIN_DEFCLASS);
+ }
+ }
+
+ /* Setup the user's group permissions */
+ if (flags & LOGIN_SETGROUP) {
+ if (setgid(pwd->pw_gid) != 0) {
+ syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid);
+ login_close(llc);
+ return -1;
+ }
+ if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
+ syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name,
+ (u_long)pwd->pw_gid);
+ login_close(llc);
+ return -1;
+ }
+ }
+
+ /* Set up the user's MAC label. */
+ if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) {
+ const char *label_string;
+ mac_t label;
+
+ label_string = login_getcapstr(lc, "label", NULL, NULL);
+ if (label_string != NULL) {
+ if (mac_from_text(&label, label_string) == -1) {
+ syslog(LOG_ERR, "mac_from_text('%s') for %s: %m",
+ pwd->pw_name, label_string);
+ return -1;
+ }
+ if (mac_set_proc(label) == -1)
+ error = errno;
+ else
+ error = 0;
+ mac_free(label);
+ if (error != 0) {
+ syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s",
+ label_string, pwd->pw_name, strerror(error));
+ return -1;
+ }
+ }
+ }
+
+ /* Set the sessions login */
+ if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) {
+ syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name);
+ login_close(llc);
+ return -1;
+ }
+
+ mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0;
+ mymask = setlogincontext(lc, pwd, mymask, flags);
+ login_close(llc);
+
+ /* This needs to be done after anything that needs root privs */
+ if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) {
+ syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid);
+ return -1; /* Paranoia again */
+ }
+
+ /*
+ * Now, we repeat some of the above for the user's private entries
+ */
+ if ((lc = login_getuserclass(pwd)) != NULL) {
+ mymask = setlogincontext(lc, pwd, mymask, flags);
+ login_close(lc);
+ }
+
+ /* Finally, set any umask we've found */
+ if (flags & LOGIN_SETUMASK)
+ umask(mymask);
+
+ return 0;
+}
+
diff --git a/lib/libutil/login_crypt.c b/lib/libutil/login_crypt.c
new file mode 100644
index 0000000..c65fc9b
--- /dev/null
+++ b/lib/libutil/login_crypt.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2000 Brian Fundakowski Feldman
+ * 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 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <login_cap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+const char *
+login_setcryptfmt(login_cap_t *lc, const char *def, const char *error) {
+ const char *cipher;
+
+ cipher = login_getcapstr(lc, "passwd_format", def, NULL);
+ if (getenv("CRYPT_DEBUG") != NULL)
+ fprintf(stderr, "login_setcryptfmt: "
+ "passwd_format = %s\n", cipher);
+ if (cipher == NULL)
+ return (error);
+ if (!crypt_set_format(cipher))
+ return (error);
+ return (cipher);
+}
diff --git a/lib/libutil/login_ok.3 b/lib/libutil/login_ok.3
new file mode 100644
index 0000000..8a6f291
--- /dev/null
+++ b/lib/libutil/login_ok.3
@@ -0,0 +1,142 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 2, 1997
+.Os
+.Dt LOGIN_OK 3
+.Sh NAME
+.Nm auth_ttyok ,
+.Nm auth_hostok ,
+.Nm auth_timeok
+.Nd functions for checking login class based login restrictions
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In time.h
+.In login_cap.h
+.Ft int
+.Fn auth_ttyok "login_cap_t *lc" "const char *tty"
+.Ft int
+.Fn auth_hostok "login_cap_t *lc" "const char *host" "char const *ip"
+.Ft int
+.Fn auth_timeok "login_cap_t *lc" "time_t t"
+.Sh DESCRIPTION
+This set of functions checks to see if login is allowed based on login
+class capability entries in the login database,
+.Xr login.conf 5 .
+.Pp
+The
+.Fn auth_ttyok
+function checks to see if the named tty is available to users of a specific
+class, and is either in the
+.Em ttys.allow
+access list, and not in
+the
+.Em ttys.deny
+access list.
+An empty
+.Em ttys.allow
+list (or if no such capability exists for
+the give login class) logins via any tty device are allowed unless
+the
+.Em ttys.deny
+list exists and is non-empty, and the device or its
+tty group (see
+.Xr ttys 5 )
+is not in the list.
+Access to ttys may be allowed or restricted specifically by tty device
+name, a device name which includes a wildcard (e.g.\& ttyD* or cuaD*),
+or may name a ttygroup, when group=<name> tags have been assigned in
+.Pa /etc/ttys .
+Matching of ttys and ttygroups is case sensitive.
+Passing a
+.Dv NULL
+or empty string as the
+.Ar tty
+parameter causes the function to return a non-zero value.
+.Pp
+The
+.Fn auth_hostok
+function checks for any host restrictions for remote logins.
+The function checks on both a host name and IP address (given in its
+text form, typically n.n.n.n) against the
+.Em host.allow
+and
+.Em host.deny
+login class capabilities.
+As with ttys and their groups, wildcards and character classes may be
+used in the host allow and deny capability records.
+The
+.Xr fnmatch 3
+function is used for matching, and the matching on hostnames is case
+insensitive.
+Note that this function expects that the hostname is fully expanded
+(i.e., the local domain name added if necessary) and the IP address
+is in its canonical form.
+No hostname or address lookups are attempted.
+.Pp
+It is possible to call this function with either the hostname or
+the IP address missing (i.e.\&
+.Dv NULL )
+and matching will be performed
+only on the basis of the parameter given.
+Passing
+.Dv NULL
+or empty strings in both parameters will result in
+a non-zero return value.
+.Pp
+The
+.Fn auth_timeok
+function checks to see that a given time value is within the
+.Em times.allow
+login class capability and not within the
+.Em times.deny
+access lists.
+An empty or non-existent
+.Em times.allow
+list allows access at any
+time, except if a given time is falls within a period in the
+.Em times.deny
+list.
+The format of time period records contained in both
+.Em times.allow
+and
+.Em times.deny
+capability fields is explained in detail in the
+.Xr login_times 3
+manual page.
+.Sh RETURN VALUES
+A non-zero return value from any of these functions indicates that
+login access is granted.
+A zero return value means either that the item being tested is not
+in the
+.Em allow
+access list, or is within the
+.Em deny
+access list.
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login_times 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_ok.c b/lib/libutil/login_ok.c
new file mode 100644
index 0000000..26971a9
--- /dev/null
+++ b/lib/libutil/login_ok.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1996 by
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Support allow/deny lists in login class capabilities
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <login_cap.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ttyent.h>
+#include <unistd.h>
+
+
+/* -- support functions -- */
+
+/*
+ * login_strinlist()
+ * This function is intentionally public - reused by TAS.
+ * Returns TRUE (non-zero) if a string matches a pattern
+ * in a given array of patterns. 'flags' is passed directly
+ * to fnmatch(3).
+ */
+
+int
+login_strinlist(const char **list, char const *str, int flags)
+{
+ int rc = 0;
+
+ if (str != NULL && *str != '\0') {
+ int i = 0;
+
+ while (rc == 0 && list[i] != NULL)
+ rc = fnmatch(list[i++], str, flags) == 0;
+ }
+ return rc;
+}
+
+
+/*
+ * login_str2inlist()
+ * Locate either or two strings in a given list
+ */
+
+int
+login_str2inlist(const char **ttlst, const char *str1, const char *str2, int flags)
+{
+ int rc = 0;
+
+ if (login_strinlist(ttlst, str1, flags))
+ rc = 1;
+ else if (login_strinlist(ttlst, str2, flags))
+ rc = 1;
+ return rc;
+}
+
+
+/*
+ * login_timelist()
+ * This function is intentionally public - reused by TAS.
+ * Returns an allocated list of time periods given an array
+ * of time periods in ascii form.
+ */
+
+login_time_t *
+login_timelist(login_cap_t *lc, char const *cap, int *ltno,
+ login_time_t **ltptr)
+{
+ int j = 0;
+ struct login_time *lt = NULL;
+ const char **tl;
+
+ if ((tl = login_getcaplist(lc, cap, NULL)) != NULL) {
+
+ while (tl[j++] != NULL)
+ ;
+ if (*ltno >= j)
+ lt = *ltptr;
+ else if ((lt = realloc(*ltptr, j * sizeof(struct login_time))) != NULL) {
+ *ltno = j;
+ *ltptr = lt;
+ }
+ if (lt != NULL) {
+ int i = 0;
+
+ for (--j; i < j; i++)
+ lt[i] = parse_lt(tl[i]);
+ lt[i].lt_dow = LTM_NONE;
+ }
+ }
+ return lt;
+}
+
+
+/*
+ * login_ttyok()
+ * This function is a variation of auth_ttyok(), but it checks two
+ * arbitrary capability lists not necessarily related to access.
+ * This hook is provided for the accounted/exclude accounting lists.
+ */
+
+int
+login_ttyok(login_cap_t *lc, const char *tty, const char *allowcap,
+ const char *denycap)
+{
+ int rc = 1;
+
+ if (lc != NULL && tty != NULL && *tty != '\0') {
+ struct ttyent *te;
+ char *grp;
+ const char **ttl;
+
+ te = getttynam(tty); /* Need group name */
+ grp = te ? te->ty_group : NULL;
+ ttl = login_getcaplist(lc, allowcap, NULL);
+
+ if (ttl != NULL && !login_str2inlist(ttl, tty, grp, 0))
+ rc = 0; /* tty or ttygroup not in allow list */
+ else {
+
+ ttl = login_getcaplist(lc, denycap, NULL);
+ if (ttl != NULL && login_str2inlist(ttl, tty, grp, 0))
+ rc = 0; /* tty or ttygroup in deny list */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * auth_ttyok()
+ * Determine whether or not login on a tty is accessible for
+ * a login class
+ */
+
+int
+auth_ttyok(login_cap_t *lc, const char * tty)
+{
+ return login_ttyok(lc, tty, "ttys.allow", "ttys.deny");
+}
+
+
+/*
+ * login_hostok()
+ * This function is a variation of auth_hostok(), but it checks two
+ * arbitrary capability lists not necessarily related to access.
+ * This hook is provided for the accounted/exclude accounting lists.
+ */
+
+int
+login_hostok(login_cap_t *lc, const char *host, const char *ip,
+ const char *allowcap, const char *denycap)
+{
+ int rc = 1; /* Default is ok */
+
+ if (lc != NULL &&
+ ((host != NULL && *host != '\0') || (ip != NULL && *ip != '\0'))) {
+ const char **hl;
+
+ hl = login_getcaplist(lc, allowcap, NULL);
+ if (hl != NULL && !login_str2inlist(hl, host, ip, FNM_CASEFOLD))
+ rc = 0; /* host or IP not in allow list */
+ else {
+
+ hl = login_getcaplist(lc, denycap, NULL);
+ if (hl != NULL && login_str2inlist(hl, host, ip, FNM_CASEFOLD))
+ rc = 0; /* host or IP in deny list */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * auth_hostok()
+ * Determine whether or not login from a host is ok
+ */
+
+int
+auth_hostok(login_cap_t *lc, const char *host, const char *ip)
+{
+ return login_hostok(lc, host, ip, "host.allow", "host.deny");
+}
+
+
+/*
+ * auth_timeok()
+ * Determine whether or not login is ok at a given time
+ */
+
+int
+auth_timeok(login_cap_t *lc, time_t t)
+{
+ int rc = 1; /* Default is ok */
+
+ if (lc != NULL && t != (time_t)0 && t != (time_t)-1) {
+ struct tm *tptr;
+
+ static int ltimesno = 0;
+ static struct login_time *ltimes = NULL;
+
+ if ((tptr = localtime(&t)) != NULL) {
+ struct login_time *lt;
+
+ lt = login_timelist(lc, "times.allow", &ltimesno, &ltimes);
+ if (lt != NULL && in_ltms(lt, tptr, NULL) == -1)
+ rc = 0; /* not in allowed times list */
+ else {
+
+ lt = login_timelist(lc, "times.deny", &ltimesno, &ltimes);
+ if (lt != NULL && in_ltms(lt, tptr, NULL) != -1)
+ rc = 0; /* in deny times list */
+ }
+ if (ltimes) {
+ free(ltimes);
+ ltimes = NULL;
+ ltimesno = 0;
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/lib/libutil/login_times.3 b/lib/libutil/login_times.3
new file mode 100644
index 0000000..e1f57ce
--- /dev/null
+++ b/lib/libutil/login_times.3
@@ -0,0 +1,161 @@
+.\" Copyright (c) 1995 David Nugent <davidn@blaze.net.au>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, is permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice immediately at the beginning of the file, without modification,
+.\" 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. This work was done expressly for inclusion into FreeBSD. Other use
+.\" is permitted provided this notation is included.
+.\" 4. Absolutely no warranty of function or purpose is made by the author
+.\" David Nugent.
+.\" 5. Modifications may be freely made to this file providing the above
+.\" conditions are met.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 2, 1997
+.Os
+.Dt LOGIN_TIMES 3
+.Sh NAME
+.Nm parse_lt ,
+.Nm in_ltm ,
+.Nm in_ltms
+.Nd functions for parsing and checking login time periods
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In time.h
+.In login_cap.h
+.Ft login_time_t
+.Fn parse_lt "const char *str"
+.Ft int
+.Fn in_ltm "const login_time_t *lt" "struct tm *t" "time_t *ends"
+.Ft int
+.Fn in_ltms "const login_time_t *lt" "struct tm *t" "time_t *ends"
+.Sh DESCRIPTION
+This set of functions may be used for parsing and checking login and
+session times against a predefined list of allowed login times as
+used in
+.Xr login.conf 5 .
+.Pp
+The format of allowed and disallowed session times specified in the
+.Ar times.allow
+and
+.Ar times.deny
+capability fields in a login class are comprised of a prefix which
+specifies one or more 2- or 3-character day codes, followed by
+a start and end time in 24 hour format separated by a hyphen.
+Day codes may be concatenated together to select specific days, or
+the special mnemonics "Any" and "All" (for any/all days of the week),
+"Wk" for any day of the week (excluding Saturdays and Sundays) and
+"Wd" for any weekend day may be used.
+.Pp
+For example, the following time period:
+.Dl MoThFrSa1400-2200
+is interpreted as Monday, Thursday through Saturday between the hours
+of 2pm and 10pm.
+.Dl Wd0600-1800
+means Saturday and Sunday, between the hours of 6am through 6pm, and
+.Dl Any0400-1600
+means any day of the week, between 4am and 4pm.
+.Pp
+Note that all time periods reference system local time.
+.Pp
+The
+.Fn parse_lt
+function converts the ASCII representation of a time period into
+a structure of type
+.Ft login_time_t .
+This is defined as:
+.Bd -literal
+typedef struct login_time
+{
+ u_short lt_start; /* Start time */
+ u_short lt_end; /* End time */
+ u_char lt_dow; /* Days of week */
+} login_time_t;
+.Ed
+.Pp
+The
+.Ar lt_start
+and
+.Ar lt_end
+fields contain the number of minutes past midnight at which the
+described period begins and ends.
+The
+.Ar lt_dow
+field is a bit field, containing one bit for each day of the week
+and one bit unused.
+A series
+.Em LTM_*
+macros may be used for testing bits individually and in combination.
+If no bits are set in this field - i.e., it contains the value
+.Em LTM_NONE
+- then the entire period is assumed invalid.
+This is used as a convention to mark the termination of an array
+of login_time_t values.
+If
+.Fn parse_lt
+returns a
+.Ar login_time_t
+with
+.Ar lt_dow
+equal to
+.Em LTM_NONE
+then a parsing error was encountered.
+.Pp
+The remaining functions provide the ability to test a given time_t or
+struct tm value against a specific time period or array of time
+periods.
+The
+.Fn in_ltm
+function determines whether the given time described by the struct tm
+passed as the second parameter falls within the period described
+by the first parameter.
+A boolean value is returned, indicating whether or not the time
+specified falls within the period.
+If the time does fall within the time period, and the third
+parameter to the function is not NULL, the time at which the
+period ends relative to the time passed is returned.
+.Pp
+The
+.Fn in_ltms
+function is similar to
+.Fn in_ltm
+except that the first parameter must be a pointer to an array
+of login_time_t objects, which is up to LC_MAXTIMES (64)
+elements in length, and terminated by an element with its
+.Ar lt_dow
+field set to
+.Em LTM_NONE .
+.Sh RETURN VALUES
+The
+.Fn parse_lt
+function returns a filled in structure of type login_time_t containing the
+parsed time period.
+If a parsing error occurs, the lt_dow field is set to
+.Em LTM_NONE
+(i.e., 0).
+.Pp
+The
+.Fn in_ltm
+function returns non-zero if the given time falls within the period described
+by the login_time_t passed as the first parameter.
+.Pp
+The
+.Fn in_ltms
+function returns the index of the first time period found in which the given
+time falls, or -1 if none of them apply.
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr login_cap 3 ,
+.Xr login_class 3 ,
+.Xr login.conf 5 ,
+.Xr termcap 5
diff --git a/lib/libutil/login_times.c b/lib/libutil/login_times.c
new file mode 100644
index 0000000..10e88dd
--- /dev/null
+++ b/lib/libutil/login_times.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1996 by
+ * David Nugent <davidn@blaze.net.au>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Login period parsing and comparison functions.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <login_cap.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+static struct
+{
+ const char *dw;
+ u_char cn;
+ u_char fl;
+} dws[] =
+{
+ { "su", 2, LTM_SUN }, { "mo", 2, LTM_MON }, { "tu", 2, LTM_TUE },
+ { "we", 2, LTM_WED }, { "th", 2, LTM_THU }, { "fr", 2, LTM_FRI },
+ { "sa", 2, LTM_SAT }, { "any",3, LTM_ANY }, { "all",3, LTM_ANY },
+ { "wk", 2, LTM_WK }, { "wd", 2, LTM_WD }, { NULL, 0, 0 }
+};
+
+static char *
+parse_time(char * ptr, u_short * t)
+{
+ u_short val;
+
+ for (val = 0; *ptr && isdigit(*ptr); ptr++)
+ val = (u_short)(val * 10 + (*ptr - '0'));
+
+ *t = (u_short)((val / 100) * 60 + (val % 100));
+
+ return ptr;
+}
+
+
+login_time_t
+parse_lt(const char * str)
+{
+ login_time_t t;
+
+ memset(&t, 0, sizeof t);
+ t.lt_dow = LTM_NONE;
+ if (str && *str && strcmp(str, "Never") != 0 && strcmp(str, "None") != 0) {
+ int i;
+ login_time_t m = t;
+ char *p;
+ char buf[64];
+
+ /* Make local copy and force lowercase to simplify parsing */
+ p = strncpy(buf, str, sizeof buf);
+ buf[sizeof buf - 1] = '\0';
+ for (i = 0; buf[i]; i++)
+ buf[i] = (char)tolower(buf[i]);
+
+ while (isalpha(*p)) {
+
+ i = 0;
+ while (dws[i].dw && strncmp(p, dws[i].dw, dws[i].cn) != 0)
+ i++;
+ if (dws[i].dw == NULL)
+ break;
+ m.lt_dow |= dws[i].fl;
+ p += dws[i].cn;
+ }
+
+ if (m.lt_dow == LTM_NONE) /* No (valid) prefix, assume any */
+ m.lt_dow |= LTM_ANY;
+
+ if (isdigit(*p))
+ p = parse_time(p, &m.lt_start);
+ else
+ m.lt_start = 0;
+ if (*p == '-')
+ p = parse_time(++p, &m.lt_end);
+ else
+ m.lt_end = 1440;
+
+ t = m;
+ }
+ return t;
+}
+
+
+int
+in_ltm(const login_time_t * ltm, struct tm * tt, time_t * ends)
+{
+ int rc = 0;
+
+ if (tt != NULL) {
+ /* First, examine the day of the week */
+ if ((u_char)(0x01 << tt->tm_wday) & ltm->lt_dow) {
+ /* Convert `current' time to minute of the day */
+ u_short now = (u_short)((tt->tm_hour * 60) + tt->tm_min);
+
+ if (tt->tm_sec > 30)
+ ++now;
+ if (now >= ltm->lt_start && now < ltm->lt_end) {
+ rc = 2;
+ if (ends != NULL) {
+ /* If requested, return ending time for this period */
+ tt->tm_hour = (int)(ltm->lt_end / 60);
+ tt->tm_min = (int)(ltm->lt_end % 60);
+ *ends = mktime(tt);
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+
+int
+in_lt(const login_time_t * ltm, time_t * t)
+{
+ return in_ltm(ltm, localtime(t), t);
+}
+
+int
+in_ltms(const login_time_t * ltm, struct tm * tm, time_t * t)
+{
+ int i = 0;
+
+ while (i < LC_MAXTIMES && ltm[i].lt_dow != LTM_NONE) {
+ if (in_ltm(ltm + i, tm, t))
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+int
+in_lts(const login_time_t * ltm, time_t * t)
+{
+ return in_ltms(ltm, localtime(t), t);
+}
+
diff --git a/lib/libutil/login_tty.3 b/lib/libutil/login_tty.3
new file mode 100644
index 0000000..6303d68
--- /dev/null
+++ b/lib/libutil/login_tty.3
@@ -0,0 +1,65 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Os
+.Dt LOGIN_TTY 3
+.Sh NAME
+.Nm login_tty
+.Nd prepare a tty for a new login session
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In libutil.h
+.Ft int
+.Fn login_tty "int fd"
+.Sh DESCRIPTION
+The function
+.Fn login_tty
+prepares a terminal for a new login session.
+The file descriptor
+.Ar fd
+passed to
+.Fn login_tty
+must be opened for reading and writing on a terminal device.
+It will be
+made the controlling terminal for the calling process, after allocating
+a new session with
+.Xr setsid 2 .
+This terminal device will also be made the standard input, standard output,
+and standard error output of the calling process.
+.Sh RETURN VALUES
+The
+.Fn login_tty
+function returns -1 if it could not make the device referenced by
+.Ar fd
+the controlling terminal of the calling process, and 0 otherwise.
+.Sh SEE ALSO
+.Xr dup2 2 ,
+.Xr ioctl 2 ,
+.Xr setsid 2 ,
+.Xr tty 4
diff --git a/lib/libutil/login_tty.c b/lib/libutil/login_tty.c
new file mode 100644
index 0000000..985a6ea
--- /dev/null
+++ b/lib/libutil/login_tty.c
@@ -0,0 +1,62 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)login_tty.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <libutil.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+login_tty(int fd)
+{
+ (void) setsid();
+ if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1)
+ return (-1);
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ if (fd > 2)
+ (void) close(fd);
+ return (0);
+}
diff --git a/lib/libutil/logout.3 b/lib/libutil/logout.3
new file mode 100644
index 0000000..426fe08
--- /dev/null
+++ b/lib/libutil/logout.3
@@ -0,0 +1,69 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Os
+.Dt LOGOUT 3
+.Sh NAME
+.Nm logout
+.Nd remove an entry from the utmp file
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft int
+.Fn logout "const char *line"
+.Sh DESCRIPTION
+The function
+.Fn logout
+searches the
+.Xr utmp 5
+file for the slot described by
+.Ar line
+(usually a tty name).
+If such a slot could be found, it will be updated
+with a record where the
+.Em name
+and
+.Em host
+fields are empty, and the time stamp field is updated to the current time.
+.Sh RETURN VALUES
+The
+.Fn logout
+function returns 1 if the slot described by
+.Ar line
+has been found and updated, 0 otherwise.
+.Sh SEE ALSO
+.Xr login 3 ,
+.Xr utmp 5 ,
+.Xr wtmp 5
+.Sh BUGS
+The calling interface of
+.Fn logout
+is inconsistent with that of
+.Xr login 3 .
diff --git a/lib/libutil/logout.c b/lib/libutil/logout.c
new file mode 100644
index 0000000..292f898
--- /dev/null
+++ b/lib/libutil/logout.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)logout.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <utmp.h>
+
+typedef struct utmp UTMP;
+
+int
+logout(const char *line)
+{
+ int fd;
+ UTMP ut;
+ int rval;
+
+ if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0)
+ return(0);
+ rval = 0;
+ while (read(fd, &ut, sizeof(UTMP)) == sizeof(UTMP)) {
+ if (!ut.ut_name[0] || strncmp(ut.ut_line, line, UT_LINESIZE))
+ continue;
+ bzero(ut.ut_name, UT_NAMESIZE);
+ bzero(ut.ut_host, UT_HOSTSIZE);
+ ut.ut_time = _time_to_time32(time(NULL));
+ (void)lseek(fd, -(off_t)sizeof(UTMP), L_INCR);
+ (void)write(fd, &ut, sizeof(UTMP));
+ rval = 1;
+ }
+ (void)close(fd);
+ return(rval);
+}
diff --git a/lib/libutil/logwtmp.3 b/lib/libutil/logwtmp.3
new file mode 100644
index 0000000..16903fb
--- /dev/null
+++ b/lib/libutil/logwtmp.3
@@ -0,0 +1,71 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Os
+.Dt LOGWTMP 3
+.Sh NAME
+.Nm logwtmp
+.Nd append a new record to the wtmp file
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft void
+.Fn logwtmp "const char *line" "const char *name" "const char *host"
+.Sh DESCRIPTION
+The function
+.Fn logwtmp
+tries to append a new record to the
+.Xr wtmp 5
+file, using the provided arguments
+.Ar line ,
+.Ar name ,
+and
+.Ar host ,
+and the current time.
+.Pp
+If the length of the hostname string
+.Ar host
+is longer than what would fit into the hostname field of the
+.Xr wtmp 5
+file, it will first be attempted to convert it into a numerical IP
+address using
+.Xr gethostbyname 3 .
+Failing this, the hostname will be recorded as
+.Qq invalid hostname .
+.Pp
+The calling process must have permission to write to the
+.Xr wtmp 5
+file.
+.Sh RETURN VALUES
+None.
+.Sh SEE ALSO
+.Xr gethostbyname 3 ,
+.Xr login 3 ,
+.Xr wtmp 5
diff --git a/lib/libutil/logwtmp.c b/lib/libutil/logwtmp.c
new file mode 100644
index 0000000..42f3599
--- /dev/null
+++ b/lib/libutil/logwtmp.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <libutil.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <utmp.h>
+
+
+void
+logwtmp(const char *line, const char *name, const char *host)
+{
+ struct utmp ut;
+ struct stat buf;
+ char fullhost[MAXHOSTNAMELEN];
+ int fd;
+
+ strncpy(fullhost, host, sizeof(fullhost) - 1);
+ fullhost[sizeof(fullhost) - 1] = '\0';
+ trimdomain(fullhost, UT_HOSTSIZE);
+ host = fullhost;
+
+ if (strlen(host) > UT_HOSTSIZE) {
+ int error;
+ struct addrinfo hints, *res;
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(host, NULL, &hints, &res);
+ if (error != 0 || res->ai_addr == NULL)
+ host = "invalid hostname";
+ else {
+ error = getnameinfo(res->ai_addr, res->ai_addrlen,
+ fullhost, strlen(fullhost), NULL, 0,
+ NI_NUMERICHOST);
+ if (error != 0) {
+ fprintf(stderr, "%d", error);
+ host = "invalid hostname";
+ }
+ }
+ }
+
+ if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ (void) strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+ (void) strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+ ut.ut_time = _time_to_time32(time(NULL));
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void) ftruncate(fd, buf.st_size);
+ }
+ (void) close(fd);
+}
diff --git a/lib/libutil/pidfile.3 b/lib/libutil/pidfile.3
new file mode 100644
index 0000000..64f77b0
--- /dev/null
+++ b/lib/libutil/pidfile.3
@@ -0,0 +1,249 @@
+.\" Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.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 AUTHORS 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 AUTHORS 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 22, 2005
+.Dt PIDFILE 3
+.Os
+.Sh NAME
+.Nm pidfile_open ,
+.Nm pidfile_write ,
+.Nm pidfile_close ,
+.Nm pidfile_remove
+.Nd "library for PID files handling"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/param.h
+.In libutil.h
+.Ft "struct pidfh *"
+.Fn pidfile_open "const char *path" "mode_t mode" "pid_t *pidptr"
+.Ft int
+.Fn pidfile_write "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_close "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_remove "struct pidfh *pfh"
+.Sh DESCRIPTION
+The
+.Nm pidfile
+family of functions allows daemons to handle PID files.
+It uses
+.Xr flock 2
+to lock a pidfile and detect already running daemons.
+.Pp
+The
+.Fn pidfile_open
+function opens (or creates) a file specified by the
+.Fa path
+argument and locks it with the
+.Xr flock 2
+system call.
+If a file can not be locked, a PID of an already running daemon is returned in
+the
+.Fa pidptr
+argument (if it is not
+.Dv NULL ) .
+The function does not write process' PID into the file here, so it can be
+used before
+.Fn fork Ns ing
+and exit with a proper error message when needed.
+If the
+.Fa path
+argument is
+.Dv NULL ,
+.Pa /var/run/ Ns Ao Va progname Ac Ns Pa .pid
+file will be used.
+.Pp
+The
+.Fn pidfile_write
+function writes process' PID into a previously opened file.
+.Pp
+The
+.Fn pidfile_close
+function closes a pidfile.
+It should be used after daemon
+.Fn fork Ns s
+to start a child process.
+.Pp
+The
+.Fn pidfile_remove
+function closes and removes a pidfile.
+.Sh RETURN VALUES
+The
+.Fn pidfile_open
+function returns a valid pointer to a
+.Vt pidfh
+structure on success, or
+.Dv NULL
+if an error occurs.
+If an error occurs,
+.Va errno
+will be set.
+.Rv -std pidfile_write pidfile_close pidfile_remove
+.Sh EXAMPLES
+The following example shows in which order these functions should be used.
+Note that it is safe to pass
+.Dv NULL
+to
+.Fn pidfile_write ,
+.Fn pidfile_remove
+and
+.Fn pidfile_close
+functions.
+.Bd -literal
+struct pidfh *pfh;
+pid_t otherpid, childpid;
+
+pfh = pidfile_open("/var/run/daemon.pid", 0600, &otherpid);
+if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(EXIT_FAILURE, "Daemon already running, pid: %jd.",
+ (intmax_t)otherpid);
+ }
+ /* If we cannot create pidfile from other reasons, only warn. */
+ warn("Cannot open or create pidfile");
+}
+
+if (daemon(0, 0) == -1) {
+ warn("Cannot daemonize");
+ pidfile_remove(pfh);
+ exit(EXIT_FAILURE);
+}
+
+pidfile_write(pfh);
+
+for (;;) {
+ /* Do work. */
+ childpid = fork();
+ switch (childpid) {
+ case -1:
+ syslog(LOG_ERR, "Cannot fork(): %s.", strerror(errno));
+ break;
+ case 0:
+ pidfile_close(pfh);
+ /* Do child work. */
+ break;
+ default:
+ syslog(LOG_INFO, "Child %jd started.", (intmax_t)childpid);
+ break;
+ }
+}
+
+pidfile_remove(pfh);
+exit(EXIT_SUCCESS);
+.Ed
+.Sh ERRORS
+The
+.Fn pidfile_open
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EEXIST
+Some process already holds the lock on the given pidfile, meaning that a
+daemon is already running.
+.It Bq Er ENAMETOOLONG
+Specified pidfile's name is too long.
+.It Bq Er EINVAL
+Some process already holds the lock on the given pidfile, but PID read
+from there is invalid.
+.El
+.Pp
+The
+.Fn pidfile_open
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr open 2 ,
+and
+.Xr read 2
+calls.
+.Pp
+The
+.Fn pidfile_write
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Improper function use.
+Probably called before
+.Fn pidfile_open .
+.El
+.Pp
+The
+.Fn pidfile_write
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr ftruncate 2 ,
+and
+.Xr write 2
+calls.
+.Pp
+The
+.Fn pidfile_close
+function may fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2
+and
+.Xr fstat 2
+calls.
+.Pp
+The
+.Fn pidfile_remove
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Improper function use.
+Probably called not from the process which made
+.Fn pidfile_write .
+.El
+.Pp
+The
+.Fn pidfile_remove
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2 ,
+.Xr flock 2 ,
+.Xr fstat 2 ,
+.Xr write 2 ,
+and
+.Xr unlink 2
+calls.
+.Sh SEE ALSO
+.Xr flock 2 ,
+.Xr open 2 ,
+.Xr daemon 3
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm pidfile
+functionality is based on ideas from
+.An John-Mark Gurney Aq jmg@FreeBSD.org .
+.Pp
+The code and manual page was written by
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org .
diff --git a/lib/libutil/pidfile.c b/lib/libutil/pidfile.c
new file mode 100644
index 0000000..cf7441e
--- /dev/null
+++ b/lib/libutil/pidfile.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.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 AUTHORS 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 AUTHORS 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+
+static int _pidfile_remove(struct pidfh *pfh, int freeit);
+
+static int
+pidfile_verify(struct pidfh *pfh)
+{
+ struct stat sb;
+ int fd;
+
+ if (pfh == NULL || pfh->pf_fd == -1)
+ return (EDOOFUS);
+ /*
+ * Check remembered descriptor.
+ */
+ if (fstat(pfh->pf_fd, &sb) == -1)
+ return (errno);
+ if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
+ return (EDOOFUS);
+ return (0);
+}
+
+static int
+pidfile_read(const char *path, pid_t *pidptr)
+{
+ char buf[16], *endptr;
+ int error, fd, i;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (errno);
+
+ i = read(fd, buf, sizeof(buf) - 1);
+ error = errno; /* Remember errno in case close() wants to change it. */
+ close(fd);
+ if (i == -1)
+ return (error);
+ buf[i] = '\0';
+
+ *pidptr = strtol(buf, &endptr, 10);
+ if (endptr != &buf[i])
+ return (EINVAL);
+
+ return (0);
+}
+
+struct pidfh *
+pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
+{
+ struct pidfh *pfh;
+ struct stat sb;
+ int error, fd;
+
+ pfh = malloc(sizeof(*pfh));
+ if (pfh == NULL)
+ return (NULL);
+
+ if (path == NULL) {
+ snprintf(pfh->pf_path, sizeof(pfh->pf_path), "/var/run/%s.pid",
+ getprogname());
+ } else {
+ strlcpy(pfh->pf_path, path, sizeof(pfh->pf_path));
+ }
+ if (strlen(pfh->pf_path) == sizeof(pfh->pf_path) - 1) {
+ free(pfh);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * Open the PID file and obtain exclusive lock.
+ * We truncate PID file here only to remove old PID immediatelly,
+ * PID file will be truncated again in pidfile_write(), so
+ * pidfile_write() can be called multiple times.
+ */
+ fd = open(pfh->pf_path,
+ O_WRONLY | O_CREAT | O_EXLOCK | O_TRUNC | O_NONBLOCK, mode);
+ if (fd == -1) {
+ if (errno == EWOULDBLOCK && pidptr != NULL) {
+ errno = pidfile_read(pfh->pf_path, pidptr);
+ if (errno == 0)
+ errno = EEXIST;
+ }
+ free(pfh);
+ return (NULL);
+ }
+ /*
+ * Remember file information, so in pidfile_write() we are sure we write
+ * to the proper descriptor.
+ */
+ if (fstat(fd, &sb) == -1) {
+ error = errno;
+ unlink(pfh->pf_path);
+ close(fd);
+ free(pfh);
+ errno = error;
+ return (NULL);
+ }
+
+ pfh->pf_fd = fd;
+ pfh->pf_dev = sb.st_dev;
+ pfh->pf_ino = sb.st_ino;
+
+ return (pfh);
+}
+
+int
+pidfile_write(struct pidfh *pfh)
+{
+ struct stat sb;
+ char pidstr[16];
+ int error, fd;
+
+ /*
+ * Check remembered descriptor, so we don't overwrite some other
+ * file if pidfile was closed and descriptor reused.
+ */
+ errno = pidfile_verify(pfh);
+ if (errno != 0) {
+ /*
+ * Don't close descriptor, because we are not sure if it's ours.
+ */
+ return (-1);
+ }
+ fd = pfh->pf_fd;
+
+ /*
+ * Truncate PID file, so multiple calls of pidfile_write() are allowed.
+ */
+ if (ftruncate(fd, 0) == -1) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ snprintf(pidstr, sizeof(pidstr), "%u", getpid());
+ if (pwrite(fd, pidstr, strlen(pidstr), 0) != (ssize_t)strlen(pidstr)) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+pidfile_close(struct pidfh *pfh)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (close(pfh->pf_fd) == -1)
+ error = errno;
+ free(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+_pidfile_remove(struct pidfh *pfh, int freeit)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (unlink(pfh->pf_path) == -1)
+ error = errno;
+ if (flock(pfh->pf_fd, LOCK_UN) == -1) {
+ if (error == 0)
+ error = errno;
+ }
+ if (close(pfh->pf_fd) == -1) {
+ if (error == 0)
+ error = errno;
+ }
+ if (freeit)
+ free(pfh);
+ else
+ pfh->pf_fd = -1;
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+pidfile_remove(struct pidfh *pfh)
+{
+
+ return (_pidfile_remove(pfh, 1));
+}
diff --git a/lib/libutil/property.3 b/lib/libutil/property.3
new file mode 100644
index 0000000..61eb153
--- /dev/null
+++ b/lib/libutil/property.3
@@ -0,0 +1,99 @@
+.\"
+.\" Copyright (c) 1998 Jordan Hubbard
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd October 7, 1998
+.Os
+.Dt PROPERTIES 3
+.Sh NAME
+.Nm properties_read ,
+.Nm propery_find ,
+.Nm properties_free
+.Nd "functions to allow creating simple property lists from ASCII file data"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft properties
+.Fn properties_read "int fd"
+.Ft char *
+.Fn property_find "properties list" "const char *name"
+.Ft void
+.Fn properties_free "properties list"
+.Sh DESCRIPTION
+.Bd -literal
+typedef struct _properties {
+ struct _properties *next;
+ char *name;
+ char *value;
+} *properties;
+.Ed
+.Pp
+The function
+.Fn properties_read
+reads
+.Fa name = value
+pairs from the file descriptor passed in
+.Fa fd
+and returns the head of a new property list, assuming that the
+file's contents have been parsed properly, or NULL in case
+of error.
+.Pp
+The
+.Fn property_find
+function returns the associated value string for the property named
+.Fa name
+if found, otherwise NULL.
+The value returned may be up to
+.Dv PROPERTY_MAX_VALUE
+bytes in length.
+.Pp
+The
+.Fn properties_free
+function is used to free the structure returned by
+.Fn properties_read
+when it is no longer needed.
+.Sh FILE FORMAT
+Each property in the file is assumed to have the format of
+.Fa name = value
+where
+.Fa name
+is an alphanumeric string (and any punctuation not including the `=' character)
+and
+.Fa value
+is an arbitary string of text terminated by a newline character.
+If newlines
+are desired, the entire value should be enclosed in { } (curly-bracket)
+characters.
+Any line beginning with a # or ; character is assumed to
+be a comment and will be ignored.
+.Sh SEE ALSO
+.Xr auth_getval 3
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh BUGS
+Simplistic.
diff --git a/lib/libutil/property.c b/lib/libutil/property.c
new file mode 100644
index 0000000..a944f9d
--- /dev/null
+++ b/lib/libutil/property.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * Simple property list handling code.
+ *
+ * Copyright (c) 1998
+ * Jordan Hubbard. 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,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 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 OR HIS PETS 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, LIFE 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static properties
+property_alloc(char *name, char *value)
+{
+ properties n;
+
+ if ((n = (properties)malloc(sizeof(struct _property))) == NULL)
+ return (NULL);
+ n->next = NULL;
+ if (name != NULL) {
+ if ((n->name = strdup(name)) == NULL) {
+ free(n);
+ return (NULL);
+ }
+ } else
+ n->name = NULL;
+ if (value != NULL) {
+ if ((n->value = strdup(value)) == NULL) {
+ free(n->name);
+ free(n);
+ return (NULL);
+ }
+ } else
+ n->value = NULL;
+ return (n);
+}
+
+properties
+properties_read(int fd)
+{
+ properties head, ptr;
+ char hold_n[PROPERTY_MAX_NAME + 1];
+ char hold_v[PROPERTY_MAX_VALUE + 1];
+ char buf[BUFSIZ * 4];
+ int bp, n, v, max;
+ enum { LOOK, COMMENT, NAME, VALUE, MVALUE, COMMIT, FILL, STOP } state, last_state;
+ int ch = 0, blevel = 0;
+
+ n = v = bp = max = 0;
+ head = ptr = NULL;
+ state = last_state = LOOK;
+ while (state != STOP) {
+ if (state != COMMIT) {
+ if (bp == max) {
+ last_state = state;
+ state = FILL;
+ } else
+ ch = buf[bp++];
+ }
+ switch(state) {
+ case FILL:
+ if ((max = read(fd, buf, sizeof buf)) < 0) {
+ properties_free(head);
+ return (NULL);
+ }
+ if (max == 0) {
+ state = STOP;
+ } else {
+ /*
+ * Restore the state from before the fill (which will be
+ * initialised to LOOK for the first FILL). This ensures that
+ * if we were part-way through eg., a VALUE state, when the
+ * buffer ran out, that the previous operation will be allowed
+ * to complete.
+ */
+ state = last_state;
+ ch = buf[0];
+ bp = 0;
+ }
+ continue;
+
+ case LOOK:
+ if (isspace((unsigned char)ch))
+ continue;
+ /* Allow shell or lisp style comments */
+ else if (ch == '#' || ch == ';') {
+ state = COMMENT;
+ continue;
+ }
+ else if (isalnum((unsigned char)ch) || ch == '_') {
+ if (n >= PROPERTY_MAX_NAME) {
+ n = 0;
+ state = COMMENT;
+ }
+ else {
+ hold_n[n++] = ch;
+ state = NAME;
+ }
+ }
+ else
+ state = COMMENT; /* Ignore the rest of the line */
+ break;
+
+ case COMMENT:
+ if (ch == '\n')
+ state = LOOK;
+ break;
+
+ case NAME:
+ if (ch == '\n' || !ch) {
+ hold_n[n] = '\0';
+ hold_v[0] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else if (isspace((unsigned char)ch))
+ continue;
+ else if (ch == '=') {
+ hold_n[n] = '\0';
+ v = n = 0;
+ state = VALUE;
+ }
+ else
+ hold_n[n++] = ch;
+ break;
+
+ case VALUE:
+ if (v == 0 && ch == '\n') {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else if (v == 0 && isspace((unsigned char)ch))
+ continue;
+ else if (ch == '{') {
+ state = MVALUE;
+ ++blevel;
+ }
+ else if (ch == '\n' || !ch) {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else {
+ if (v >= PROPERTY_MAX_VALUE) {
+ state = COMMENT;
+ v = n = 0;
+ break;
+ }
+ else
+ hold_v[v++] = ch;
+ }
+ break;
+
+ case MVALUE:
+ /* multiline value */
+ if (v >= PROPERTY_MAX_VALUE) {
+ warn("properties_read: value exceeds max length");
+ state = COMMENT;
+ n = v = 0;
+ }
+ else if (ch == '}' && !--blevel) {
+ hold_v[v] = '\0';
+ v = n = 0;
+ state = COMMIT;
+ }
+ else {
+ hold_v[v++] = ch;
+ if (ch == '{')
+ ++blevel;
+ }
+ break;
+
+ case COMMIT:
+ if (head == NULL) {
+ if ((head = ptr = property_alloc(hold_n, hold_v)) == NULL)
+ return (NULL);
+ } else {
+ if ((ptr->next = property_alloc(hold_n, hold_v)) == NULL) {
+ properties_free(head);
+ return (NULL);
+ }
+ ptr = ptr->next;
+ }
+ state = LOOK;
+ v = n = 0;
+ break;
+
+ case STOP:
+ /* we don't handle this here, but this prevents warnings */
+ break;
+ }
+ }
+ if (head == NULL && (head = property_alloc(NULL, NULL)) == NULL)
+ return (NULL);
+
+ return (head);
+}
+
+char *
+property_find(properties list, const char *name)
+{
+ if (list == NULL || name == NULL || !name[0])
+ return (NULL);
+ while (list != NULL) {
+ if (list->name != NULL && strcmp(list->name, name) == 0)
+ return (list->value);
+ list = list->next;
+ }
+ return (NULL);
+}
+
+void
+properties_free(properties list)
+{
+ properties tmp;
+
+ while (list) {
+ tmp = list->next;
+ if (list->name)
+ free(list->name);
+ if (list->value)
+ free(list->value);
+ free(list);
+ list = tmp;
+ }
+}
diff --git a/lib/libutil/pty.3 b/lib/libutil/pty.3
new file mode 100644
index 0000000..35f0b82
--- /dev/null
+++ b/lib/libutil/pty.3
@@ -0,0 +1,149 @@
+.\"
+.\" Copyright (c) 1996 Joerg Wunsch
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd December 29, 1996
+.Os
+.Dt PTY 3
+.Sh NAME
+.Nm openpty ,
+.Nm forkpty
+.Nd auxiliary functions to obtain a pseudo-terminal
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/ioctl.h
+.In termios.h
+.In libutil.h
+.Ft int
+.Fn openpty "int *amaster" "int *aslave" "char *name" "struct termios *termp" "struct winsize *winp"
+.Ft int
+.Fn forkpty "int *amaster" "char *name" "struct termios *termp" "struct winsize *winp"
+.Sh DESCRIPTION
+The function
+.Fn openpty
+attempts to obtain the next available pseudo-terminal from the system (see
+.Xr pty 4 ) .
+If it successfully finds one, it subsequently tries to change the
+ownership of the slave device to the real UID of the current process,
+the group membership to the group
+.Dq tty
+(if such a group exists in the system), the access permissions for
+reading and writing by the owner, and for writing by the group, and to
+invalidate any current use of the line by calling
+.Xr revoke 2 .
+.Pp
+If the argument
+.Fa name
+is not
+.Dv NULL ,
+.Fn openpty
+copies the pathname of the slave pty to this area.
+The caller is
+responsible for allocating the required space in this array.
+.Pp
+If the arguments
+.Fa termp
+or
+.Fa winp
+are not
+.Dv NULL ,
+.Fn openpty
+initializes the termios and window size settings from the structures
+these arguments point to, respectively.
+.Pp
+Upon return, the open file descriptors for the master and slave side
+of the pty are returned in the locations pointed to by
+.Fa amaster
+and
+.Fa aslave ,
+respectively.
+.Pp
+The
+.Fn forkpty
+function first calls
+.Fn openpty
+to obtain the next available pseudo-terminal from the system.
+Upon success,
+it forks off a new process.
+In the child process, it closes the descriptor
+for the master side of the pty, and calls
+.Xr login_tty 3
+for the slave pty.
+In the parent process, it closes the descriptor for the
+slave side of the pty.
+The arguments
+.Fa amaster ,
+.Fa name ,
+.Fa termp ,
+and
+.Fa winp
+have the same meaning as described for
+.Fn openpty .
+.Sh RETURN VALUES
+The
+.Fn openpty
+function returns 0 on success, or -1 on failure.
+.Pp
+The
+.Fn forkpty
+function returns -1 on failure, 0 in the slave process, and the process ID of
+the slave process in the parent process.
+.Sh ERRORS
+On failure,
+.Fn openpty
+will set the global variable
+.Dv errno
+to
+.Er ENOENT .
+.Pp
+In addition to this,
+.Fn forkpty
+may set it to any value as described for
+.Xr fork 2 .
+.Sh SEE ALSO
+.Xr chmod 2 ,
+.Xr chown 2 ,
+.Xr fork 2 ,
+.Xr getuid 2 ,
+.Xr open 2 ,
+.Xr revoke 2 ,
+.Xr login_tty 3 ,
+.Xr pty 4 ,
+.Xr termios 4 ,
+.Xr group 5
+.Sh BUGS
+The calling process must have an effective UID of super-user in order
+to perform all the intended actions.
+No notification will occur if
+.Fn openpty
+or
+.Fn forkpty
+failed to proceed with one of the described steps, as long as they could
+at least allocate the pty at all (and create the new process in the case
+of
+.Fn forkpty ) .
diff --git a/lib/libutil/pty.c b/lib/libutil/pty.c
new file mode 100644
index 0000000..553468c
--- /dev/null
+++ b/lib/libutil/pty.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)pty.c 8.3 (Berkeley) 5/16/94";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libutil.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+int __use_pts(void);
+
+static int
+new_openpty(int *amaster, int *aslave, char *name, struct termios *termp,
+ struct winsize *winp)
+{
+ int master, slave;
+
+ master = posix_openpt(O_RDWR);
+ if (master == -1)
+ return (-1);
+
+ if (grantpt(master) == -1) {
+ close(master);
+ return (-1);
+ }
+
+ slave = open(ptsname(master), O_RDWR);
+ if (slave == -1) {
+ close(master);
+ return (-1);
+ }
+
+ if (unlockpt(master) == -1) {
+ close(master);
+ close(slave);
+ return (-1);
+ }
+
+ *amaster = master;
+ *aslave = slave;
+
+ if (name)
+ strcpy(name, ptsname(master));
+ if (termp)
+ tcsetattr(slave, TCSAFLUSH, termp);
+ if (winp)
+ ioctl(slave, TIOCSWINSZ, (char *)winp);
+
+ return (0);
+}
+
+int
+openpty(int *amaster, int *aslave, char *name, struct termios *termp, struct winsize *winp)
+{
+ char line[] = "/dev/ptyXX";
+ const char *cp1, *cp2;
+ int master, slave, ttygid;
+ struct group *gr;
+
+ if (__use_pts())
+ return (new_openpty(amaster, aslave, name, termp, winp));
+
+ if ((gr = getgrnam("tty")) != NULL)
+ ttygid = gr->gr_gid;
+ else
+ ttygid = -1;
+
+ for (cp1 = "pqrsPQRS"; *cp1; cp1++) {
+ line[8] = *cp1;
+ for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) {
+ line[5] = 'p';
+ line[9] = *cp2;
+ if ((master = open(line, O_RDWR, 0)) == -1) {
+ if (errno == ENOENT)
+ break; /* try the next pty group */
+ } else {
+ line[5] = 't';
+ (void) chown(line, getuid(), ttygid);
+ (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
+ (void) revoke(line);
+ if ((slave = open(line, O_RDWR, 0)) != -1) {
+ *amaster = master;
+ *aslave = slave;
+ if (name)
+ strcpy(name, line);
+ if (termp)
+ (void) tcsetattr(slave,
+ TCSAFLUSH, termp);
+ if (winp)
+ (void) ioctl(slave, TIOCSWINSZ,
+ (char *)winp);
+ return (0);
+ }
+ (void) close(master);
+ }
+ }
+ }
+ errno = ENOENT; /* out of ptys */
+ return (-1);
+}
+
+int
+forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp)
+{
+ int master, slave, pid;
+
+ if (openpty(&master, &slave, name, termp, winp) == -1)
+ return (-1);
+ switch (pid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ /*
+ * child
+ */
+ (void) close(master);
+ login_tty(slave);
+ return (0);
+ }
+ /*
+ * parent
+ */
+ *amaster = master;
+ (void) close(slave);
+ return (pid);
+}
diff --git a/lib/libutil/pw_util.c b/lib/libutil/pw_util.c
new file mode 100644
index 0000000..90c9d72
--- /dev/null
+++ b/lib/libutil/pw_util.c
@@ -0,0 +1,616 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libutil.h>
+
+static pid_t editpid = -1;
+static int lockfd = -1;
+static char masterpasswd[PATH_MAX];
+static char passwd_dir[PATH_MAX];
+static char tempname[PATH_MAX];
+static int initialized;
+
+#if 0
+void
+pw_cont(int sig)
+{
+
+ if (editpid != -1)
+ kill(editpid, sig);
+}
+#endif
+
+/*
+ * Initialize statics and set limits, signals & umask to try to avoid
+ * interruptions, crashes etc. that might expose passord data.
+ */
+int
+pw_init(const char *dir, const char *master)
+{
+#if 0
+ struct rlimit rlim;
+#endif
+
+ if (dir == NULL) {
+ strcpy(passwd_dir, _PATH_ETC);
+ } else {
+ if (strlen(dir) >= sizeof(passwd_dir)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(passwd_dir, dir);
+ }
+
+ if (master == NULL) {
+ if (dir == NULL) {
+ strcpy(masterpasswd, _PATH_MASTERPASSWD);
+ } else if (snprintf(masterpasswd, sizeof(masterpasswd), "%s/%s",
+ passwd_dir, _MASTERPASSWD) > (int)sizeof(masterpasswd)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ } else {
+ if (strlen(master) >= sizeof(masterpasswd)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ strcpy(masterpasswd, master);
+ }
+
+ /*
+ * The code that follows is extremely disruptive to the calling
+ * process, and is therefore disabled until someone can conceive
+ * of a realistic scenario where it would fend off a compromise.
+ * Race conditions concerning the temporary files can be guarded
+ * against in other ways than masking signals (by checking stat(2)
+ * results after creation).
+ */
+#if 0
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGCONT, pw_cont);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+#endif
+ initialized = 1;
+ return (0);
+}
+
+/*
+ * Lock the master password file.
+ */
+int
+pw_lock(void)
+{
+
+ if (*masterpasswd == '\0')
+ return (-1);
+
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ for (;;) {
+ struct stat st;
+
+ lockfd = open(masterpasswd, O_RDONLY, 0);
+ if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1)
+ err(1, "%s", masterpasswd);
+ /* XXX vulnerable to race conditions */
+ if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
+ if (errno == EWOULDBLOCK) {
+ errx(1, "the password db file is busy");
+ } else {
+ err(1, "could not lock the passwd file: ");
+ }
+ }
+
+ /*
+ * If the password file was replaced while we were trying to
+ * get the lock, our hardlink count will be 0 and we have to
+ * close and retry.
+ */
+ if (fstat(lockfd, &st) == -1)
+ err(1, "fstat() failed: ");
+ if (st.st_nlink != 0)
+ break;
+ close(lockfd);
+ lockfd = -1;
+ }
+ return (lockfd);
+}
+
+/*
+ * Create and open a presumably safe temp file for editing the password
+ * data, and copy the master password file into it.
+ */
+int
+pw_tmp(int mfd)
+{
+ char buf[8192];
+ ssize_t nr;
+ const char *p;
+ int tfd;
+
+ if (*masterpasswd == '\0')
+ return (-1);
+ if ((p = strrchr(masterpasswd, '/')))
+ ++p;
+ else
+ p = masterpasswd;
+ if (snprintf(tempname, sizeof(tempname), "%.*spw.XXXXXX",
+ (int)(p - masterpasswd), masterpasswd) >= (int)sizeof(tempname)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ if ((tfd = mkstemp(tempname)) == -1)
+ return (-1);
+ if (mfd != -1) {
+ while ((nr = read(mfd, buf, sizeof(buf))) > 0)
+ if (write(tfd, buf, (size_t)nr) != nr)
+ break;
+ if (nr != 0) {
+ unlink(tempname);
+ *tempname = '\0';
+ close(tfd);
+ return (-1);
+ }
+ }
+ return (tfd);
+}
+
+/*
+ * Regenerate the password database.
+ */
+int
+pw_mkdb(const char *user)
+{
+ int pstat;
+ pid_t pid;
+
+ (void)fflush(stderr);
+ switch ((pid = fork())) {
+ case -1:
+ return (-1);
+ case 0:
+ /* child */
+ if (user == NULL)
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+ "-d", passwd_dir, tempname, (char *)NULL);
+ else
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
+ "-d", passwd_dir, "-u", user, tempname,
+ (char *)NULL);
+ _exit(1);
+ /* NOTREACHED */
+ default:
+ /* parent */
+ break;
+ }
+ if (waitpid(pid, &pstat, 0) == -1)
+ return (-1);
+ if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
+ return (0);
+ errno = 0;
+ return (-1);
+}
+
+/*
+ * Edit the temp file. Return -1 on error, >0 if the file was modified, 0
+ * if it was not.
+ */
+int
+pw_edit(int notsetuid)
+{
+ struct sigaction sa, sa_int, sa_quit;
+ sigset_t oldsigset, sigset;
+ struct stat st1, st2;
+ const char *editor;
+ int pstat;
+
+ if ((editor = getenv("EDITOR")) == NULL)
+ editor = _PATH_VI;
+ if (stat(tempname, &st1) == -1)
+ return (-1);
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, &sa_int);
+ sigaction(SIGQUIT, &sa, &sa_quit);
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigset, &oldsigset);
+ switch ((editpid = fork())) {
+ case -1:
+ return (-1);
+ case 0:
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ if (notsetuid) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+ errno = 0;
+ execlp(editor, basename(editor), tempname, (char *)NULL);
+ _exit(errno);
+ default:
+ /* parent */
+ break;
+ }
+ for (;;) {
+ if (waitpid(editpid, &pstat, WUNTRACED) == -1) {
+ if (errno == EINTR)
+ continue;
+ unlink(tempname);
+ editpid = -1;
+ break;
+ } else if (WIFSTOPPED(pstat)) {
+ raise(WSTOPSIG(pstat));
+ } else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) {
+ editpid = -1;
+ break;
+ } else {
+ unlink(tempname);
+ editpid = -1;
+ break;
+ }
+ }
+ sigaction(SIGINT, &sa_int, NULL);
+ sigaction(SIGQUIT, &sa_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldsigset, NULL);
+ if (stat(tempname, &st2) == -1)
+ return (-1);
+ return (st1.st_mtime != st2.st_mtime);
+}
+
+/*
+ * Clean up. Preserve errno for the caller's convenience.
+ */
+void
+pw_fini(void)
+{
+ int serrno, status;
+
+ if (!initialized)
+ return;
+ initialized = 0;
+ serrno = errno;
+ if (editpid != -1) {
+ kill(editpid, SIGTERM);
+ kill(editpid, SIGCONT);
+ waitpid(editpid, &status, 0);
+ editpid = -1;
+ }
+ if (*tempname != '\0') {
+ unlink(tempname);
+ *tempname = '\0';
+ }
+ if (lockfd != -1)
+ close(lockfd);
+ errno = serrno;
+}
+
+/*
+ * Compares two struct pwds.
+ */
+int
+pw_equal(const struct passwd *pw1, const struct passwd *pw2)
+{
+ return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
+ pw1->pw_uid == pw2->pw_uid &&
+ pw1->pw_gid == pw2->pw_gid &&
+ strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
+ pw1->pw_change == pw2->pw_change &&
+ pw1->pw_expire == pw2->pw_expire &&
+ strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
+ strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
+ strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
+}
+
+/*
+ * Make a passwd line out of a struct passwd.
+ */
+char *
+pw_make(const struct passwd *pw)
+{
+ char *line;
+
+ asprintf(&line, "%s:%s:%ju:%ju:%s:%ju:%ju:%s:%s:%s", pw->pw_name,
+ pw->pw_passwd, (uintmax_t)pw->pw_uid, (uintmax_t)pw->pw_gid,
+ pw->pw_class, (uintmax_t)pw->pw_change, (uintmax_t)pw->pw_expire,
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ return line;
+}
+
+/*
+ * Copy password file from one descriptor to another, replacing or adding
+ * a single record on the way.
+ */
+int
+pw_copy(int ffd, int tfd, const struct passwd *pw, struct passwd *old_pw)
+{
+ char buf[8192], *end, *line, *p, *q, *r, t;
+ struct passwd *fpw;
+ size_t len;
+ int eof, readlen;
+
+ if ((line = pw_make(pw)) == NULL)
+ return (-1);
+
+ eof = 0;
+ len = 0;
+ p = q = end = buf;
+ for (;;) {
+ /* find the end of the current line */
+ for (p = q; q < end && *q != '\0'; ++q)
+ if (*q == '\n')
+ break;
+
+ /* if we don't have a complete line, fill up the buffer */
+ if (q >= end) {
+ if (eof)
+ break;
+ if ((size_t)(q - p) >= sizeof(buf)) {
+ warnx("passwd line too long");
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ if (p < end) {
+ q = memmove(buf, p, end - p);
+ end -= p - buf;
+ } else {
+ p = q = end = buf;
+ }
+ readlen = read(ffd, end, sizeof(buf) - (end - buf));
+ if (readlen == -1)
+ goto err;
+ else
+ len = (size_t)readlen;
+ if (len == 0 && p == buf)
+ break;
+ end += len;
+ len = end - buf;
+ if (len < (ssize_t)sizeof(buf)) {
+ eof = 1;
+ if (len > 0 && buf[len - 1] != '\n')
+ ++len, *end++ = '\n';
+ }
+ continue;
+ }
+
+ /* is it a blank line or a comment? */
+ for (r = p; r < q && isspace(*r); ++r)
+ /* nothing */ ;
+ if (r == q || *r == '#') {
+ /* yep */
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+
+ /* is it the one we're looking for? */
+ t = *q;
+ *q = '\0';
+ fpw = pw_scan(r, PWSCAN_MASTER);
+ *q = t;
+ if (strcmp(fpw->pw_name, pw->pw_name) != 0) {
+ /* nope */
+ free(fpw);
+ if (write(tfd, p, q - p + 1) != q - p + 1)
+ goto err;
+ ++q;
+ continue;
+ }
+ if (old_pw && !pw_equal(fpw, old_pw)) {
+ warnx("entry inconsistent");
+ free(fpw);
+ errno = EINVAL; /* hack */
+ goto err;
+ }
+ free(fpw);
+
+ /* it is, replace it */
+ len = strlen(line);
+ if (write(tfd, line, len) != (int)len)
+ goto err;
+
+ /* we're done, just copy the rest over */
+ for (;;) {
+ if (write(tfd, q, end - q) != end - q)
+ goto err;
+ q = buf;
+ readlen = read(ffd, buf, sizeof(buf));
+ if (readlen == 0)
+ break;
+ else
+ len = (size_t)readlen;
+ if (readlen == -1)
+ goto err;
+ end = buf + len;
+ }
+ goto done;
+ }
+
+ /* if we got here, we have a new entry */
+ len = strlen(line);
+ if ((size_t)write(tfd, line, len) != len ||
+ write(tfd, "\n", 1) != 1)
+ goto err;
+ done:
+ free(line);
+ return (0);
+ err:
+ free(line);
+ return (-1);
+}
+
+/*
+ * Return the current value of tempname.
+ */
+const char *
+pw_tempname(void)
+{
+
+ return (tempname);
+}
+
+/*
+ * Duplicate a struct passwd.
+ */
+struct passwd *
+pw_dup(const struct passwd *pw)
+{
+ struct passwd *npw;
+ ssize_t len;
+
+ len = sizeof(*npw) +
+ (pw->pw_name ? strlen(pw->pw_name) + 1 : 0) +
+ (pw->pw_passwd ? strlen(pw->pw_passwd) + 1 : 0) +
+ (pw->pw_class ? strlen(pw->pw_class) + 1 : 0) +
+ (pw->pw_gecos ? strlen(pw->pw_gecos) + 1 : 0) +
+ (pw->pw_dir ? strlen(pw->pw_dir) + 1 : 0) +
+ (pw->pw_shell ? strlen(pw->pw_shell) + 1 : 0);
+ if ((npw = malloc((size_t)len)) == NULL)
+ return (NULL);
+ memcpy(npw, pw, sizeof(*npw));
+ len = sizeof(*npw);
+ if (pw->pw_name) {
+ npw->pw_name = ((char *)npw) + len;
+ len += sprintf(npw->pw_name, "%s", pw->pw_name) + 1;
+ }
+ if (pw->pw_passwd) {
+ npw->pw_passwd = ((char *)npw) + len;
+ len += sprintf(npw->pw_passwd, "%s", pw->pw_passwd) + 1;
+ }
+ if (pw->pw_class) {
+ npw->pw_class = ((char *)npw) + len;
+ len += sprintf(npw->pw_class, "%s", pw->pw_class) + 1;
+ }
+ if (pw->pw_gecos) {
+ npw->pw_gecos = ((char *)npw) + len;
+ len += sprintf(npw->pw_gecos, "%s", pw->pw_gecos) + 1;
+ }
+ if (pw->pw_dir) {
+ npw->pw_dir = ((char *)npw) + len;
+ len += sprintf(npw->pw_dir, "%s", pw->pw_dir) + 1;
+ }
+ if (pw->pw_shell) {
+ npw->pw_shell = ((char *)npw) + len;
+ len += sprintf(npw->pw_shell, "%s", pw->pw_shell) + 1;
+ }
+ return (npw);
+}
+
+#include "pw_scan.h"
+
+/*
+ * Wrapper around an internal libc function
+ */
+struct passwd *
+pw_scan(const char *line, int flags)
+{
+ struct passwd pw, *ret;
+ char *bp;
+
+ if ((bp = strdup(line)) == NULL)
+ return (NULL);
+ if (!__pw_scan(bp, &pw, flags)) {
+ free(bp);
+ return (NULL);
+ }
+ ret = pw_dup(&pw);
+ free(bp);
+ return (ret);
+}
diff --git a/lib/libutil/realhostname.3 b/lib/libutil/realhostname.3
new file mode 100644
index 0000000..fd64d4c
--- /dev/null
+++ b/lib/libutil/realhostname.3
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.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 AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 6, 1999
+.Os
+.Dt REALHOSTNAME 3
+.Sh NAME
+.Nm realhostname
+.Nd "convert an IP number to the real host name"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In netinet/in.h
+.In libutil.h
+.Ft int
+.Fn realhostname "char *host" "size_t hsize" "const struct in_addr *ip"
+.Sh DESCRIPTION
+The function
+.Fn realhostname
+converts
+.Ar ip
+to the corresponding host name.
+This is done by resolving
+.Ar ip
+to a host name and then ensuring that the host name resolves
+back to
+.Ar ip .
+.Pp
+.Ar host
+must point to a buffer of at least
+.Ar hsize
+bytes, and will always be written to by this function.
+.Pp
+If the name resolution does not work both ways or if the host name is longer
+than
+.Ar hsize
+bytes,
+.Xr inet_ntoa 3
+is used to convert
+.Ar ip
+to an ASCII form.
+.Pp
+If the string written to
+.Ar host
+is
+.Ar hsize
+bytes long,
+.Ar host
+will not be NUL terminated.
+.Sh RETURN VALUES
+The
+.Fn realhostname
+function will return one of the following constants which are defined in
+.In libutil.h :
+.Pp
+.Bl -tag -width XXX -offset XXX
+.It Li HOSTNAME_FOUND
+A valid host name was found.
+.It Li HOSTNAME_INCORRECTNAME
+A host name was found, but it did not resolve back to the passed
+.Ar ip .
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDADDR
+.Ar ip
+could not be resolved.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDNAME
+A host name was found, but it could not be resolved back to any ip number.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.El
+.Sh SEE ALSO
+.Xr gethostbyaddr 3 ,
+.Xr gethostbyname 3 ,
+.Xr inet_ntoa 3 ,
+.Xr realhostname_sa 3
diff --git a/lib/libutil/realhostname.c b/lib/libutil/realhostname.c
new file mode 100644
index 0000000..6a1b82f
--- /dev/null
+++ b/lib/libutil/realhostname.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libutil.h"
+
+struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
+};
+
+int
+realhostname(char *host, size_t hsize, const struct in_addr *ip)
+{
+ char trimmed[MAXHOSTNAMELEN];
+ int result;
+ struct hostent *hp;
+
+ result = HOSTNAME_INVALIDADDR;
+ hp = gethostbyaddr((const char *)ip, sizeof(*ip), AF_INET);
+
+ if (hp != NULL) {
+ strlcpy(trimmed, hp->h_name, sizeof(trimmed));
+ trimdomain(trimmed, strlen(trimmed));
+ if (strlen(trimmed) <= hsize) {
+ char lookup[MAXHOSTNAMELEN];
+
+ strncpy(lookup, hp->h_name, sizeof(lookup) - 1);
+ lookup[sizeof(lookup) - 1] = '\0';
+ hp = gethostbyname(lookup);
+ if (hp == NULL)
+ result = HOSTNAME_INVALIDNAME;
+ else for (; ; hp->h_addr_list++) {
+ if (*hp->h_addr_list == NULL) {
+ result = HOSTNAME_INCORRECTNAME;
+ break;
+ }
+ if (!memcmp(*hp->h_addr_list, ip, sizeof(*ip))) {
+ strncpy(host, trimmed, hsize);
+ return HOSTNAME_FOUND;
+ }
+ }
+ }
+ }
+
+ strncpy(host, inet_ntoa(*ip), hsize);
+
+ return result;
+}
+
+int
+realhostname_sa(char *host, size_t hsize, struct sockaddr *addr, int addrlen)
+{
+ int result, error;
+ char buf[NI_MAXHOST];
+ struct sockaddr_in lsin;
+
+ result = HOSTNAME_INVALIDADDR;
+
+#ifdef INET6
+ /* IPv4 mapped IPv6 addr consideraton, specified in rfc2373. */
+ if (addr->sa_family == AF_INET6 &&
+ addrlen == sizeof(struct sockaddr_in6) &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)addr;
+
+ memset(&lsin, 0, sizeof(lsin));
+ lsin.sin_len = sizeof(struct sockaddr_in);
+ lsin.sin_family = AF_INET;
+ lsin.sin_port = sin6->sin6_port;
+ memcpy(&lsin.sin_addr, &sin6->sin6_addr.s6_addr[12],
+ sizeof(struct in_addr));
+ addr = (struct sockaddr *)&lsin;
+ addrlen = lsin.sin_len;
+ }
+#endif
+
+ error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NAMEREQD);
+ if (error == 0) {
+ struct addrinfo hints, *res, *ores;
+ struct sockaddr *sa;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = addr->sa_family;
+ hints.ai_flags = AI_CANONNAME | AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo(buf, NULL, &hints, &res);
+ if (error) {
+ result = HOSTNAME_INVALIDNAME;
+ goto numeric;
+ }
+ for (ores = res; ; res = res->ai_next) {
+ if (res == NULL) {
+ freeaddrinfo(ores);
+ result = HOSTNAME_INCORRECTNAME;
+ goto numeric;
+ }
+ sa = res->ai_addr;
+ if (sa == NULL) {
+ freeaddrinfo(ores);
+ result = HOSTNAME_INCORRECTNAME;
+ goto numeric;
+ }
+ if (sa->sa_len == addrlen &&
+ sa->sa_family == addr->sa_family) {
+ ((struct sockinet *)sa)->si_port = ((struct sockinet *)addr)->si_port;
+#ifdef INET6
+ /*
+ * XXX: sin6_socpe_id may not been
+ * filled by DNS
+ */
+ if (sa->sa_family == AF_INET6 &&
+ ((struct sockaddr_in6 *)sa)->sin6_scope_id == 0)
+ ((struct sockaddr_in6 *)sa)->sin6_scope_id = ((struct sockaddr_in6 *)addr)->sin6_scope_id;
+#endif
+ if (!memcmp(sa, addr, sa->sa_len)) {
+ result = HOSTNAME_FOUND;
+ if (ores->ai_canonname == NULL) {
+ freeaddrinfo(ores);
+ goto numeric;
+ }
+ strlcpy(buf, ores->ai_canonname,
+ sizeof(buf));
+ trimdomain(buf, hsize);
+ if (strlen(buf) > hsize &&
+ addr->sa_family == AF_INET) {
+ freeaddrinfo(ores);
+ goto numeric;
+ }
+ strncpy(host, buf, hsize);
+ break;
+ }
+ }
+ }
+ freeaddrinfo(ores);
+ } else {
+ numeric:
+ if (getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
+ NI_NUMERICHOST) == 0)
+ strncpy(host, buf, hsize);
+ }
+
+ return result;
+}
+
+
diff --git a/lib/libutil/realhostname_sa.3 b/lib/libutil/realhostname_sa.3
new file mode 100644
index 0000000..9f7f0df
--- /dev/null
+++ b/lib/libutil/realhostname_sa.3
@@ -0,0 +1,133 @@
+.\" Copyright (C) 1995, 1996, 1997, 1998, 1999, and 2000 WIDE Project.
+.\" 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 project 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 PROJECT 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 PROJECT 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.
+.\"
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.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 AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 11, 2000
+.Os
+.Dt REALHOSTNAME_SA 3
+.Sh NAME
+.Nm realhostname_sa
+.Nd "convert a"
+.Vt "struct sockaddr"
+to the real host name
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In netinet/in.h
+.In libutil.h
+.Ft int
+.Fn realhostname_sa "char *host" "size_t hsize" "struct sockaddr *addr" "int addrlen"
+.Sh DESCRIPTION
+The function
+.Fn realhostname_sa
+converts
+.Ar addr
+to the corresponding host name.
+This is done by resolving
+.Ar addr
+to a host name and then ensuring that the host name resolves
+back to
+.Ar addr .
+.Pp
+.Ar host
+must point to a buffer of at least
+.Ar hsize
+bytes, and will always be written to by this function.
+.Pp
+If the name resolution does not work both ways or if the host name is longer
+than
+.Ar hsize
+bytes,
+.Xr getnameinfo 3
+with NI_NUMERICHOST specified, is used to convert
+.Ar addr
+to an ASCII form.
+.Pp
+If the string written to
+.Ar host
+is
+.Ar hsize
+bytes long,
+.Ar host
+will not be NUL terminated.
+.Sh RETURN VALUES
+The
+.Fn realhostname_sa
+function will return one of the following constants which are defined in
+.In libutil.h :
+.Pp
+.Bl -tag -width XXX -offset XXX
+.It Li HOSTNAME_FOUND
+A valid host name was found.
+.It Li HOSTNAME_INCORRECTNAME
+A host name was found, but it did not resolve back to the passed
+.Ar ip .
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDADDR
+.Ar ip
+could not be resolved.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.It Li HOSTNAME_INVALIDNAME
+A host name was found, but it could not be resolved back to any ip number.
+.Ar host
+now contains the numeric value of
+.Ar ip .
+.El
+.Sh SEE ALSO
+.Xr getaddrinfo 3 ,
+.Xr getnameinfo 3 ,
+.Xr realhostname 3
diff --git a/lib/libutil/stub.c b/lib/libutil/stub.c
new file mode 100644
index 0000000..ec78a35
--- /dev/null
+++ b/lib/libutil/stub.c
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2000 Brian Fundakowski Feldman
+ * 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 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * Stub out what's in -lcrypt.
+ */
+
+#pragma weak crypt_set_format
+/* ARGSUSED */
+int
+crypt_set_format(const char *f __unused) {
+
+ if (getenv("CRYPT_DEBUG") != NULL)
+ fprintf(stderr, "crypt_set_format: eek, stub called!\n");
+ return (0);
+}
diff --git a/lib/libutil/trimdomain.3 b/lib/libutil/trimdomain.3
new file mode 100644
index 0000000..f01e884
--- /dev/null
+++ b/lib/libutil/trimdomain.3
@@ -0,0 +1,85 @@
+.\" Copyright (c) 1999 Brian Somers <brian@Awfulhak.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 AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 7, 1999
+.Os
+.Dt TRIMDOMAIN 3
+.Sh NAME
+.Nm trimdomain
+.Nd "trim the current domain name from a host name"
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft void
+.Fn trimdomain "char *fullhost" "int hostsize"
+.Sh DESCRIPTION
+The function
+.Fn trimdomain
+removes the current domain name from the passed
+.Ar fullhost
+name by writing a
+.Dv NUL
+character over the first period of the passed name.
+The current domain
+name is determined by calling
+.Xr gethostname 3
+and removing everything up to the first period.
+The name is determined
+the first time this function is called and is cached for future use.
+.Pp
+The
+.Fn trimdomain
+function will only trim the domain name if the passed
+.Ar fullname
+ends with the current domain name and if the length of the resulting host
+name does not exceed
+.Ar hostsize .
+.Pp
+If the passed
+.Ar fullname
+is actually a
+.Dv DISPLAY
+specification of the form
+.Sm off
+.Ar host . domain : nn Oo .
+.Ar nn
+.Oc
+.Sm on
+and the domain name is the same as the local domain name,
+.Fn trimdomain
+will remove the embedded domain name, copying the screen and display
+numbers to the end of the base host name and resulting in
+.Sm off
+.Ar host : nn Op . Ar nn .
+.Sm on
+.Sh RETURN VALUES
+The
+.Fn trimdomain
+function does not return a value.
+.Sh SEE ALSO
+.Xr gethostname 3
diff --git a/lib/libutil/trimdomain.c b/lib/libutil/trimdomain.c
new file mode 100644
index 0000000..85c27e0
--- /dev/null
+++ b/lib/libutil/trimdomain.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * Based on original work by Atsushi Murai <amurai@FreeBSD.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 AUTHOR 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 AUTHOR 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <libutil.h>
+#include <string.h>
+#include <unistd.h>
+
+static int isDISP(const char *);
+
+/*-
+ * Trim the current domain name from fullhost, but only if the result
+ * is less than or equal to hostsize in length.
+ *
+ * This function understands $DISPLAY type fullhosts.
+ *
+ * For example:
+ *
+ * trimdomain("abcde.my.domain", 5) -> "abcde"
+ * trimdomain("abcde.my.domain", 4) -> "abcde.my.domain"
+ * trimdomain("abcde.my.domain:0.0", 9) -> "abcde:0.0"
+ * trimdomain("abcde.my.domain:0.0", 8) -> "abcde.my.domain:0.0"
+ */
+void
+trimdomain(char *fullhost, int hostsize)
+{
+ static size_t dlen;
+ static int first = 1;
+ static char domain[MAXHOSTNAMELEN];
+ char *end, *s;
+ size_t len;
+
+ if (first) {
+ /* XXX: Should we assume that our domain is this persistent ? */
+ first = 0;
+ if (gethostname(domain, sizeof(domain) - 1) == 0 &&
+ (s = strchr(domain, '.')) != NULL)
+ memmove(domain, s + 1, strlen(s + 1) + 1);
+ else
+ domain[0] = '\0';
+ dlen = strlen(domain);
+ }
+
+ if (domain[0] == '\0')
+ return;
+
+ s = fullhost;
+ end = s + hostsize + 1;
+ if ((s = memchr(s, '.', (size_t)(end - s))) != NULL) {
+ if (strncasecmp(s + 1, domain, dlen) == 0) {
+ if (s[dlen + 1] == '\0') {
+ /* Found -- lose the domain. */
+ *s = '\0';
+ } else if (s[dlen + 1] == ':' &&
+ isDISP(s + dlen + 2) &&
+ (len = strlen(s + dlen + 1)) < (size_t)(end - s)) {
+ /* Found -- shuffle the DISPLAY back. */
+ memmove(s, s + dlen + 1, len + 1);
+ }
+ }
+ }
+}
+
+/*
+ * Is the given string NN or NN.NN where ``NN'' is an all-numeric string ?
+ */
+static int
+isDISP(const char *disp)
+{
+ size_t w;
+ int res;
+
+ w = strspn(disp, "0123456789");
+ res = 0;
+ if (w > 0) {
+ if (disp[w] == '\0')
+ res = 1; /* NN */
+ else if (disp[w] == '.') {
+ disp += w + 1;
+ w = strspn(disp, "0123456789");
+ if (w > 0 && disp[w] == '\0')
+ res = 1; /* NN.NN */
+ }
+ }
+ return (res);
+}
diff --git a/lib/libutil/uucplock.3 b/lib/libutil/uucplock.3
new file mode 100644
index 0000000..5f75c7f
--- /dev/null
+++ b/lib/libutil/uucplock.3
@@ -0,0 +1,183 @@
+.\"
+.\" Copyright (c) 1996 Brian Somers <brian@awfulhak.demon.co.uk>
+.\"
+.\" 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 DEVELOPERS ``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 DEVELOPERS 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.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd March 30, 1997
+.Os
+.Dt UUCPLOCK 3
+.Sh NAME
+.Nm uu_lock ,
+.Nm uu_unlock ,
+.Nm uu_lockerr
+.Nd acquire and release control of a serial device
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/types.h
+.In libutil.h
+.Ft int
+.Fn uu_lock "const char *ttyname"
+.Ft int
+.Fn uu_lock_txfr "const char *ttyname" "pid_t pid"
+.Ft int
+.Fn uu_unlock "const char *ttyname"
+.Ft const char *
+.Fn uu_lockerr "int uu_lockresult"
+.Sh DESCRIPTION
+The
+.Fn uu_lock
+function attempts to create a lock file called
+.Pa /var/spool/lock/LCK..
+with a suffix given by the passed
+.Fa ttyname .
+If the file already exists, it is expected to contain the process
+id of the locking program.
+.Pp
+If the file does not already exist, or the owning process given by
+the process id found in the lock file is no longer running,
+.Fn uu_lock
+will write its own process id into the file and return success.
+.Pp
+.Fn uu_lock_txfr
+transfers lock ownership to another process.
+.Fn uu_lock
+must have previously been successful.
+.Pp
+.Fn uu_unlock
+removes the lockfile created by
+.Fn uu_lock
+for the given
+.Fa ttyname .
+Care should be taken that
+.Fn uu_lock
+was successful before calling
+.Fn uu_unlock .
+.Pp
+.Fn uu_lockerr
+returns an error string representing the error
+.Fa uu_lockresult ,
+as returned from
+.Fn uu_lock .
+.Sh RETURN VALUES
+.Fn uu_unlock
+returns 0 on success and -1 on failure.
+.Pp
+.Fn uu_lock
+may return any of the following values:
+.Pp
+.Dv UU_LOCK_INUSE :
+The lock is in use by another process.
+.Pp
+.Dv UU_LOCK_OK :
+The lock was successfully created.
+.Pp
+.Dv UU_LOCK_OPEN_ERR :
+The lock file could not be opened via
+.Xr open 2 .
+.Pp
+.Dv UU_LOCK_READ_ERR :
+The lock file could not be read via
+.Xr read 2 .
+.Pp
+.Dv UU_LOCK_CREAT_ERR :
+Cannot create temporary lock file via
+.Xr creat 2 .
+.Pp
+.Dv UU_LOCK_WRITE_ERR :
+The current process id could not be written to the lock file via a call to
+.Xr write 2 .
+.Pp
+.Dv UU_LOCK_LINK_ERR :
+Cannot link temporary lock file via
+.Xr link 2 .
+.Pp
+.Dv UU_LOCK_TRY_ERR :
+Locking attempts are failed after 5 tries.
+.Pp
+If a value of
+.Dv UU_LOCK_OK
+is passed to
+.Fn uu_lockerr ,
+an empty string is returned.
+Otherwise, a string specifying
+the reason for failure is returned.
+.Fn uu_lockerr
+uses the current value of
+.Va errno
+to determine the exact error.
+Care should be made not to allow
+.Va errno
+to be changed between calls to
+.Fn uu_lock
+and
+.Fn uu_lockerr .
+.Pp
+.Fn uu_lock_txfr
+may return any of the following values:
+.Pp
+.Dv UU_LOCK_OK :
+The transfer was successful.
+The specified process now holds the device
+lock.
+.Pp
+.Dv UU_LOCK_OWNER_ERR :
+The current process does not already own a lock on the specified device.
+.Pp
+.Dv UU_LOCK_WRITE_ERR :
+The new process id could not be written to the lock file via a call to
+.Xr write 2 .
+.Sh ERRORS
+If
+.Fn uu_lock
+returns one of the error values above, the global value
+.Va errno
+can be used to determine the cause.
+Refer to the respective manual pages
+for further details.
+.Pp
+.Fn uu_unlock
+will set the global variable
+.Va errno
+to reflect the reason that the lock file could not be removed.
+Refer to the description of
+.Xr unlink 2
+for further details.
+.Sh SEE ALSO
+.Xr lseek 2 ,
+.Xr open 2 ,
+.Xr read 2 ,
+.Xr write 2
+.Sh BUGS
+It is possible that a stale lock is not recognised as such if a new
+processes is assigned the same processes id as the program that left
+the stale lock.
+.Pp
+The calling process must have write permissions to the
+.Pa /var/spool/lock
+directory.
+There is no mechanism in place to ensure that the
+permissions of this directory are the same as those of the
+serial devices that might be locked.
diff --git a/lib/libutil/uucplock.c b/lib/libutil/uucplock.c
new file mode 100644
index 0000000..71ea25e
--- /dev/null
+++ b/lib/libutil/uucplock.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 1988, 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifndef lint
+static const char sccsid[] = "@(#)uucplock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "libutil.h"
+
+#define MAXTRIES 5
+
+#define LOCKTMP "LCKTMP..%d"
+#define LOCKFMT "LCK..%s"
+
+#define GORET(level, val) { err = errno; uuerr = (val); \
+ goto __CONCAT(ret, level); }
+
+/* Forward declarations */
+static int put_pid (int fd, pid_t pid);
+static pid_t get_pid (int fd,int *err);
+
+/*
+ * uucp style locking routines
+ */
+
+int
+uu_lock(const char *tty_name)
+{
+ int fd, tmpfd, i;
+ pid_t pid, pid_old;
+ char lckname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN],
+ lcktmpname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+ int err, uuerr;
+
+ pid = getpid();
+ (void)snprintf(lcktmpname, sizeof(lcktmpname), _PATH_UUCPLOCK LOCKTMP,
+ pid);
+ (void)snprintf(lckname, sizeof(lckname), _PATH_UUCPLOCK LOCKFMT,
+ tty_name);
+ if ((tmpfd = creat(lcktmpname, 0664)) < 0)
+ GORET(0, UU_LOCK_CREAT_ERR);
+
+ for (i = 0; i < MAXTRIES; i++) {
+ if (link (lcktmpname, lckname) < 0) {
+ if (errno != EEXIST)
+ GORET(1, UU_LOCK_LINK_ERR);
+ /*
+ * file is already locked
+ * check to see if the process holding the lock
+ * still exists
+ */
+ if ((fd = open(lckname, O_RDONLY)) < 0)
+ GORET(1, UU_LOCK_OPEN_ERR);
+
+ if ((pid_old = get_pid (fd, &err)) == -1)
+ GORET(2, UU_LOCK_READ_ERR);
+
+ close(fd);
+
+ if (kill(pid_old, 0) == 0 || errno != ESRCH)
+ GORET(1, UU_LOCK_INUSE);
+ /*
+ * The process that locked the file isn't running, so
+ * we'll lock it ourselves
+ */
+ (void)unlink(lckname);
+ } else {
+ if (!put_pid (tmpfd, pid))
+ GORET(3, UU_LOCK_WRITE_ERR);
+ break;
+ }
+ }
+ GORET(1, (i >= MAXTRIES) ? UU_LOCK_TRY_ERR : UU_LOCK_OK);
+
+ret3:
+ (void)unlink(lckname);
+ goto ret1;
+ret2:
+ (void)close(fd);
+ret1:
+ (void)close(tmpfd);
+ (void)unlink(lcktmpname);
+ret0:
+ errno = err;
+ return uuerr;
+}
+
+int
+uu_lock_txfr(const char *tty_name, pid_t pid)
+{
+ int fd, err;
+ char lckname[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ snprintf(lckname, sizeof(lckname), _PATH_UUCPLOCK LOCKFMT, tty_name);
+
+ if ((fd = open(lckname, O_RDWR)) < 0)
+ return UU_LOCK_OWNER_ERR;
+ if (get_pid(fd, &err) != getpid())
+ err = UU_LOCK_OWNER_ERR;
+ else {
+ lseek(fd, (off_t)0, SEEK_SET);
+ err = put_pid(fd, pid) ? 0 : UU_LOCK_WRITE_ERR;
+ }
+ close(fd);
+
+ return err;
+}
+
+int
+uu_unlock(const char *tty_name)
+{
+ char tbuf[sizeof(_PATH_UUCPLOCK) + MAXNAMLEN];
+
+ (void)snprintf(tbuf, sizeof(tbuf), _PATH_UUCPLOCK LOCKFMT, tty_name);
+ return unlink(tbuf);
+}
+
+const char *
+uu_lockerr(int uu_lockresult)
+{
+ static char errbuf[128];
+ const char *fmt;
+
+ switch (uu_lockresult) {
+ case UU_LOCK_INUSE:
+ return "device in use";
+ case UU_LOCK_OK:
+ return "";
+ case UU_LOCK_OPEN_ERR:
+ fmt = "open error: %s";
+ break;
+ case UU_LOCK_READ_ERR:
+ fmt = "read error: %s";
+ break;
+ case UU_LOCK_CREAT_ERR:
+ fmt = "creat error: %s";
+ break;
+ case UU_LOCK_WRITE_ERR:
+ fmt = "write error: %s";
+ break;
+ case UU_LOCK_LINK_ERR:
+ fmt = "link error: %s";
+ break;
+ case UU_LOCK_TRY_ERR:
+ fmt = "too many tries: %s";
+ break;
+ case UU_LOCK_OWNER_ERR:
+ fmt = "not locking process: %s";
+ break;
+ default:
+ fmt = "undefined error: %s";
+ break;
+ }
+
+ (void)snprintf(errbuf, sizeof(errbuf), fmt, strerror(errno));
+ return errbuf;
+}
+
+static int
+put_pid(int fd, pid_t pid)
+{
+ char buf[32];
+ int len;
+
+ len = sprintf (buf, "%10d\n", (int)pid);
+ return write (fd, buf, (size_t)len) == len;
+}
+
+static pid_t
+get_pid(int fd, int *err)
+{
+ int bytes_read;
+ char buf[32];
+ pid_t pid;
+
+ bytes_read = read (fd, buf, sizeof (buf) - 1);
+ if (bytes_read > 0) {
+ buf[bytes_read] = '\0';
+ pid = (pid_t)strtol (buf, (char **) NULL, 10);
+ } else {
+ pid = -1;
+ *err = bytes_read ? errno : EINVAL;
+ }
+ return pid;
+}
+
+/* end of uucplock.c */
OpenPOWER on IntegriCloud