aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuncaen <mail@duncano.de>2017-03-08 23:35:00 +0100
committerDuncaen <mail@duncano.de>2017-03-08 23:35:00 +0100
commit8075444f49d822ddec4e2020b2b93a833ac63790 (patch)
tree4e72095261776979b083b6b95be21ad191a2e085
parentb26cb592042f9942ca6e4c3022e5df3623739089 (diff)
downloadlobase-8075444f49d822ddec4e2020b2b93a833ac63790.tar.gz
usr.bin/htpasswd: import
-rw-r--r--include/pwd.h2
-rw-r--r--usr.bin/Makefile2
-rw-r--r--usr.bin/htpasswd/Makefile13
-rw-r--r--usr.bin/htpasswd/htpasswd.174
-rw-r--r--usr.bin/htpasswd/htpasswd.c231
5 files changed, 321 insertions, 1 deletions
diff --git a/include/pwd.h b/include/pwd.h
index ef8f643..59ab03a 100644
--- a/include/pwd.h
+++ b/include/pwd.h
@@ -43,6 +43,8 @@
#include <sys/types.h>
+#define _PASSWORD_LEN 128 /* max length, not counting NUL */
+
char *user_from_uid(uid_t, int);
char *bcrypt_gensalt(u_int8_t);
char *bcrypt(const char *, const char *);
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
index d2d9eb8..ca9c33e 100644
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -5,6 +5,6 @@ SUBDIR= apply awk basename bc biff cal calendar cmp colrm col column comm \
mktemp nice nl nohup paste patch printenv printf readlink renice rev \
rs sed shar sort spell split stat tee time touch tr true tsort tty ul \
units uname unexpand uniq unvis uudecode uuencode vis wc what which \
- xinstall hexdump cu newsyslog sdiff
+ xinstall hexdump htpasswd cu newsyslog sdiff
SKIPDIR=file cu
include ${.TOPDIR}/mk/bsd.subdir.mk
diff --git a/usr.bin/htpasswd/Makefile b/usr.bin/htpasswd/Makefile
new file mode 100644
index 0000000..f76d827
--- /dev/null
+++ b/usr.bin/htpasswd/Makefile
@@ -0,0 +1,13 @@
+# $OpenBSD: Makefile,v 1.1 2014/03/17 12:49:13 florian Exp $
+
+.TOPDIR?=../..
+
+PROG= htpasswd
+SRCS= htpasswd.c
+MAN= htpasswd.1
+
+CFLAGS+= -g -W -Wall -Werror
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare
+
+include ${.TOPDIR}/mk/bsd.prog.mk
diff --git a/usr.bin/htpasswd/htpasswd.1 b/usr.bin/htpasswd/htpasswd.1
new file mode 100644
index 0000000..759e20d
--- /dev/null
+++ b/usr.bin/htpasswd/htpasswd.1
@@ -0,0 +1,74 @@
+.\" $OpenBSD: htpasswd.1,v 1.7 2014/08/26 21:50:38 jmc Exp $
+.\"
+.\" Copyright (c) 2014 Florian Obser <florian@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: August 26 2014 $
+.Dt HTPASSWD 1
+.Os
+.Sh NAME
+.Nm htpasswd
+.Nd create and update user authentication files
+.Sh SYNOPSIS
+.Nm
+.Op Ar file
+.Ar login
+.Nm
+.Fl I
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+is used to create and update user authentication files for
+HTTP daemons.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl I
+Switch to batch mode.
+.Nm
+reads exactly one line from standard input and splits it at the first
+.Qo : Qc .
+The first part is the login, the second part is the password which
+.Nm
+then hashes using
+.Xr bcrypt 3 .
+.El
+.Pp
+.Nm
+prompts for a password and generates a hash using
+.Xr bcrypt 3 .
+A line suitable for a password file is written to the standard output.
+If invoked with two arguments
+.Po
+or one argument if the
+.Fl I
+flag is used
+.Pc
+user authentication
+.Ar file
+is updated.
+.Sh SEE ALSO
+.Xr bcrypt 3
+.Sh HISTORY
+This reimplemented version of
+.Nm
+has been available since
+.Ox 5.6 .
+.Sh AUTHORS
+.An Florian Obser Aq Mt florian@openbsd.org
+implemented
+.Nm
+from scratch after httpd was removed from
+.Ox
+base.
diff --git a/usr.bin/htpasswd/htpasswd.c b/usr.bin/htpasswd/htpasswd.c
new file mode 100644
index 0000000..535c4e7
--- /dev/null
+++ b/usr.bin/htpasswd/htpasswd.c
@@ -0,0 +1,231 @@
+/* $OpenBSD: htpasswd.c,v 1.15 2015/11/05 20:07:15 florian Exp $ */
+/*
+ * Copyright (c) 2014 Florian Obser <florian@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define _GNU_SOURCE /* for asprintf */
+
+#include <sys/stat.h>
+#ifdef __linux__
+#include <sys/file.h> /* for flock(2) */
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <readpassphrase.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+__dead void usage(void);
+void nag(char*);
+
+extern char *__progname;
+
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage:\t%s [file] login\n", __progname);
+ fprintf(stderr, "\t%s -I [file]\n", __progname);
+ exit(1);
+}
+
+#define MAXNAG 5
+int nagcount;
+
+int
+main(int argc, char** argv)
+{
+ char salt[_PASSWORD_LEN], tmpl[sizeof("/tmp/htpasswd-XXXXXXXXXX")];
+ char hash[_PASSWORD_LEN], pass[1024], pass2[1024];
+ char *line = NULL, *login = NULL, *tok;
+ int c, fd, loginlen, batch = 0;
+ FILE *in = NULL, *out = NULL;
+ const char *file = NULL;
+ size_t linesize = 0;
+ ssize_t linelen;
+ mode_t old_umask;
+
+ if (pledge("stdio rpath wpath cpath flock tmppath tty", NULL) == -1)
+ err(1, "pledge");
+
+ while ((c = getopt(argc, argv, "I")) != -1) {
+ switch (c) {
+ case 'I':
+ batch = 1;
+ break;
+ default:
+ usage();
+ /* NOT REACHED */
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (batch) {
+ if (argc == 1)
+ file = argv[0];
+ else if (argc > 1)
+ usage();
+ else if (pledge("stdio", NULL) == -1)
+ err(1, "pledge");
+
+ if ((linelen = getline(&line, &linesize, stdin)) == -1)
+ err(1, "cannot read login:password from stdin");
+ line[linelen-1] = '\0';
+
+ if ((tok = strstr(line, ":")) == NULL)
+ errx(1, "cannot find ':' in input");
+ *tok++ = '\0';
+
+ if ((loginlen = asprintf(&login, "%s:", line)) == -1)
+ err(1, "asprintf");
+
+ if (strlcpy(pass, tok, sizeof(pass)) >= sizeof(pass))
+ errx(1, "password too long");
+ } else {
+
+ switch (argc) {
+ case 1:
+ if (pledge("stdio tty", NULL) == -1)
+ err(1, "pledge");
+ if ((loginlen = asprintf(&login, "%s:", argv[0])) == -1)
+ err(1, "asprintf");
+ break;
+ case 2:
+ file = argv[0];
+ if ((loginlen = asprintf(&login, "%s:", argv[1])) == -1)
+ err(1, "asprintf");
+ break;
+ default:
+ usage();
+ /* NOT REACHED */
+ break;
+ }
+
+ if (!readpassphrase("Password: ", pass, sizeof(pass),
+ RPP_ECHO_OFF))
+ err(1, "unable to read password");
+ if (!readpassphrase("Retype Password: ", pass2, sizeof(pass2),
+ RPP_ECHO_OFF)) {
+ explicit_bzero(pass, sizeof(pass));
+ err(1, "unable to read password");
+ }
+ if (strcmp(pass, pass2) != 0) {
+ explicit_bzero(pass, sizeof(pass));
+ explicit_bzero(pass2, sizeof(pass2));
+ errx(1, "passwords don't match");
+ }
+
+ explicit_bzero(pass2, sizeof(pass2));
+ }
+
+ if (strlcpy(salt, bcrypt_gensalt(8), sizeof(salt)) >= sizeof(salt))
+ errx(1, "salt too long");
+ if (strlcpy(hash, bcrypt(pass, salt), sizeof(hash)) >= sizeof(hash))
+ errx(1, "hash too long");
+ explicit_bzero(pass, sizeof(pass));
+
+ if (file == NULL)
+ printf("%s%s\n", login, hash);
+ else {
+ if ((in = fopen(file, "r+")) == NULL) {
+ if (errno == ENOENT) {
+ old_umask = umask(S_IXUSR|
+ S_IWGRP|S_IRGRP|S_IXGRP|
+ S_IWOTH|S_IROTH|S_IXOTH);
+ if ((out = fopen(file, "w")) == NULL)
+ err(1, "cannot open password file for"
+ " reading or writing");
+ umask(old_umask);
+ } else
+ err(1, "cannot open password file for"
+ " reading or writing");
+ } else
+ if (flock(fileno(in), LOCK_EX|LOCK_NB) == -1)
+ errx(1, "cannot lock password file");
+
+ /* file already exits, copy content and filter login out */
+ if (out == NULL) {
+ strlcpy(tmpl, "/tmp/htpasswd-XXXXXXXXXX", sizeof(tmpl));
+ if ((fd = mkstemp(tmpl)) == -1)
+ err(1, "mkstemp");
+
+ if ((out = fdopen(fd, "w+")) == NULL)
+ err(1, "cannot open tempfile");
+
+ while ((linelen = getline(&line, &linesize, in))
+ != -1) {
+ if (strncmp(line, login, loginlen) != 0) {
+ if (fprintf(out, "%s", line) == -1)
+ errx(1, "cannot write to temp "
+ "file");
+ nag(line);
+ }
+ }
+ }
+ if (fprintf(out, "%s%s\n", login, hash) == -1)
+ errx(1, "cannot write new password hash");
+
+ /* file already exists, overwrite it */
+ if (in != NULL) {
+ if (fseek(in, 0, SEEK_SET) == -1)
+ err(1, "cannot seek in password file");
+ if (fseek(out, 0, SEEK_SET) == -1)
+ err(1, "cannot seek in temp file");
+ if (ftruncate(fileno(in), 0) == -1)
+ err(1, "cannot truncate password file");
+ while ((linelen = getline(&line, &linesize, out))
+ != -1)
+ if (fprintf(in, "%s", line) == -1)
+ errx(1, "cannot write to password "
+ "file");
+ if (fclose(in) == EOF)
+ err(1, "cannot close password file");
+ }
+ if (fclose(out) == EOF) {
+ if (in != NULL)
+ err(1, "cannot close temp file");
+ else
+ err(1, "cannot close password file");
+ }
+ if (in != NULL && unlink(tmpl) == -1)
+ err(1, "cannot delete temp file (%s)", tmpl);
+ }
+ if (nagcount >= MAXNAG)
+ warnx("%d more logins not using bcryt.", nagcount - MAXNAG);
+ exit(0);
+}
+
+void
+nag(char* line)
+{
+ const char *tok;
+ if (strtok(line, ":") != NULL)
+ if ((tok = strtok(NULL, ":")) != NULL)
+ if (strncmp(tok, "$2a$", 4) != 0 &&
+ strncmp(tok, "$2b$", 4) != 0) {
+ nagcount++;
+ if (nagcount <= MAXNAG)
+ warnx("%s doesn't use bcrypt."
+ " Update the password.", line);
+ }
+}