lobase

Linux port of OpenBSDs userland.
Log | Files | Refs | README

commit 8075444f49d822ddec4e2020b2b93a833ac63790
parent b26cb592042f9942ca6e4c3022e5df3623739089
Author: Duncaen <mail@duncano.de>
Date:   Wed,  8 Mar 2017 23:35:00 +0100

usr.bin/htpasswd: import

Diffstat:
include/pwd.h | 2++
usr.bin/Makefile | 2+-
usr.bin/htpasswd/Makefile | 13+++++++++++++
usr.bin/htpasswd/htpasswd.1 | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
usr.bin/htpasswd/htpasswd.c | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 321 insertions(+), 1 deletion(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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); + } +}