commit: 6af392c004c287007566dc8a2b5f538d11844bd2
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 28 Apr 2021 01:32:40 +0200
Initial Commit
Diffstat:
A | .clang-format | 21 | +++++++++++++++++++++ |
A | .gitignore | 4 | ++++ |
A | DCO-1.1.txt | 37 | +++++++++++++++++++++++++++++++++++++ |
A | Makefile | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | README.md | 15 | +++++++++++++++ |
A | checkpassword.8 | 74 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | checkpassword.c | 82 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | chkpw.3 | 36 | ++++++++++++++++++++++++++++++++++++ |
A | chkpw.c | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | chkpw.h | 6 | ++++++ |
10 files changed, 424 insertions(+), 0 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -0,0 +1,21 @@
+AlignAfterOpenBracket: true
+AlignConsecutiveAssignments: true
+AlignOperands: true
+AlignTrailingComments: true
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: true
+AllowShortIfStatementsOnASingleLine: true
+AlwaysBreakAfterReturnType: AllDefinitions
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBraces: Allman
+SpaceBeforeParens: Never
+IncludeBlocks: Regroup
+ReflowComments: false
+SortIncludes: true
+UseTab: ForIndentation
+IndentWidth: 2
+TabWidth: 2
+ColumnLimit: 100
+
+NamespaceIndentation: All
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,4 @@
+/checkpassword
+*.o
+*.so
+*.a
diff --git a/DCO-1.1.txt b/DCO-1.1.txt
@@ -0,0 +1,37 @@
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+1 Letterman Drive
+Suite D4700
+San Francisco, CA, 94129
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,57 @@
+# POSIX-ish Makefile with extensions common to *BSD and GNU such as:
+# - Usage of backticks for shell evaluation
+# - Usage of ?= for defining variables when not already defined
+# - Usage of += for appending to a variable
+
+VERSION = 0.1.0
+VERSION_FULL = $(VERSION)`./version.sh`
+
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+LIBDIR = $(PREFIX)/lib
+MANDIR = $(PREFIX)/share/man
+
+CC ?= cc
+AR ?= ar
+EXE_CFLAGS = -pie -fPIE
+LIB_CFLAGS = -fpic -fPIC
+CFLAGS ?= -g -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wconversion -Wsign-conversion -O2 -Werror=implicit-function-declaration -Werror=implicit-int -Werror=vla
+
+LIBS = -lcrypt
+
+EXE = checkpassword
+LIBSO = libchkpw.so
+LIBA = libchkpw.a
+SRC = checkpassword.c chkpw.c
+
+all: $(EXE) $(LIBSO) $(LIBA)
+
+checkpassword: checkpassword.c chkpw.o
+ $(CC) -std=c99 $(EXE_CFLAGS) $(CFLAGS) -o $@ $< chkpw.o $(LIBS) $(LDFLAGS)
+
+libchkpw.so: chkpw.c
+ $(CC) -std=c99 -c -shared $(LIB_CFLAGS) $(CFLAGS) -o $@ $<
+
+chkpw.o: chkpw.c
+ $(CC) -std=c99 -c $(LIB_CFLAGS) $(CFLAGS) -o $@ $<
+
+libchkpw.a: chkpw.o
+ $(AR) -rv $@ $<
+
+.PHONY: install
+install: all
+ mkdir -p $(DESTDIR)$(BINDIR)
+ cp -p $(EXE) $(DESTDIR)$(BINDIR)/$(EXE)
+ mkdir -p $(DESTDIR)$(LIBDIR)
+ cp -p $(LIBSO) $(LIBA) $(DESTDIR)$(LIBDIR)/
+ mkdir -p $(DESTDIR)$(MANDIR)/man3
+ cp -p chkpwd.8 $(DESTDIR)$(MANDIR)/man3
+ mkdir -p $(DESTDIR)$(MANDIR)/man8
+ cp -p $(EXE).8 $(DESTDIR)$(MANDIR)/man8
+
+.PHONY: clean
+clean:
+ rm -fr $(EXE) chkpw.o $(LIBSO) $(LIBA)
+
+format: *.c
+ clang-format -style=file -assume-filename=.clang-format -i *.c
diff --git a/README.md b/README.md
@@ -0,0 +1,15 @@
+# checkpassword-ng - Uniform password checking interface for applications
+```
+Copyright © 2021 checkpassword-ng Authors <https://hacktivis.me/git/checkpassword-ng>
+SPDX-License-Identifier: GPL-3.0-only
+```
+
+This is a clean-room reimplementation of D. J. Bernstein [checkpassword](https://cr.yp.to/checkpwd.html) using <https://cr.yp.to/checkpwd/interface.html> as documentation.
+- The licensing status of the original is unclear but is considered to be all-rights-reserved
+- The original last version dates back to 2000 and distributions have been adding patches on top of it
+
+## Sign your work - the Developer's Certificate of Origin
+
+To make sure that everyone has proper rights on the code they are submitting, every contributor will sign-off their commits under the ` Developer's Certificate of Origin 1.1` (copy included in `DCO-1.1.txt`).
+
+Pseudonyms are tolerated but it is highly recommended to use accountable names.
diff --git a/checkpassword.8 b/checkpassword.8
@@ -0,0 +1,74 @@
+.\" checkpassword-ng: Uniform password checking interface for applications
+.\" Copyright © 2021 checkpassword-ng Authors <https://hacktivis.me/git/checkpassword-ng>
+.\" SPDX-License-Identifier: GPL-3.0-only
+.Dd 2021-04-27
+.Dt CHECKPASSWORD 8
+.Sh NAME
+.Nm checkpassword
+.Nd Uniform password checking interface for applications
+.Sh SYNOPSIS
+.Nm
+.Ar prog
+.Sh DESCRIPTION
+.Nm
+reads at most 512-octets from file descriptor 3 and then closes it.
+zero-byte-separated data is given on file descriptor 3:
+.Bl -bullet -compact
+.It
+login name
+.It
+password
+.It
+timestamp (ignored in this implementation)
+.It
+possibly more data (also ignored)
+.El
+.Pp
+.Nm
+is for applications where separating authentication to a small optionally setuid-root process makes sense.
+Typically this is:
+.Bl -bullet -compact
+.It
+screen lockers
+.It
+display managers (where root/UID-0 is excluded)
+.It
+critical user-consent dialogs
+.It
+daemons wanting to use regular logins (consider ssh-keys though)
+.El
+.Pp
+Out-of-scope applications are:
+.Bl -tag -width Ds -compact
+.It sudo, doas, …
+These applications typically allow to login as root and would only widen their attack surface by using a separated process.
+A library would be a better fit for them.
+.El
+.Pp
+Client applications are highly recommended to use a restricted
+.Ev PATH
+or a direct path to
+.Nm .
+.Sh ENVIRONMENT VARIABLES
+Ignored in this implementation, used in others to pass options.
+.Sh EXIT STATUS
+If the password is invalid,
+.Nm
+returns 1.
+If
+.Nm
+is misused, it may return 2.
+If there is a temporary problem,
+.Nm
+returns 111.
+If the password is correct,
+.Nm
+runs
+.Ar prog .
+.Sh SEE ALSO
+.Lk https://cr.yp.to/checkpwd/interface.html The checkpassword interface
+.Sh Author
+This implementation of
+.Nm
+was written from scratch by
+.An Haelwenn (lanodan) Monnier Aq Mt contact+chkpw@hacktivis.me
diff --git a/checkpassword.c b/checkpassword.c
@@ -0,0 +1,82 @@
+// checkpassword-ng: Uniform password checking interface for applications
+// Copyright © 2021 checkpassword-ng Authors <https://hacktivis.me/git/checkpassword-ng>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#define _POSIX_C_SOURCE 200809L
+// explicit_bzero
+#define _DEFAULT_SOURCE
+#include "chkpw.h"
+
+#include <assert.h> /* assert() */
+#include <stdio.h> /* fprintf, perror */
+#include <string.h> /* explicit_bzero, strlen, memcpy */
+#include <unistd.h> /* read */
+
+// At most 512 bytes of data before EOF
+#define ERR_MAX_LEN 512
+// invalid password
+#define ERR_INVALID 1
+// misused
+#define ERR_MISUSED 2
+// temporary problem
+#define ERR_ETMP 111
+
+int
+main(int argc, char *argv[])
+{
+ char input[ERR_MAX_LEN], username[ERR_MAX_LEN] = "", password[ERR_MAX_LEN] = "";
+ ssize_t bytes_read = -1;
+
+ // Note: getopt isn't used
+ if((argc < 2) && argv[1])
+ {
+ fprintf(stderr, "prog argument missing, exiting...\n");
+ return ERR_MISUSED;
+ }
+
+ argc--;
+ argv++;
+
+ bytes_read = read(3, input, ERR_MAX_LEN);
+ // At least 3 \0 plus some data
+ if(bytes_read < 3)
+ {
+ perror("read(3, _, _)");
+ return ERR_MISUSED;
+ }
+
+ char *buf = input;
+
+ memcpy(username, buf, strlen(buf));
+ printf("username = %s\n", username);
+ if(username == NULL)
+ {
+ fprintf(stderr, "couldn't extract username, exiting...\n");
+ return ERR_MISUSED;
+ }
+
+ buf += strlen(buf) + 1;
+ memcpy(password, buf, strlen(buf));
+ printf("password = %s\n", password);
+ if(password == NULL)
+ {
+ fprintf(stderr, "couldn't extract password, exiting...\n");
+ return ERR_MISUSED;
+ }
+
+ char *res = chkpw(username, password, NULL);
+ explicit_bzero(password, sizeof(password));
+
+ if(res == CHKPW_VALID)
+ {
+ assert(argv[0]);
+ execvp(argv[0], argv);
+ }
+ else
+ {
+ fprintf(stderr, "chkpw: %s\n", res);
+ return ERR_INVALID;
+ }
+
+ assert(1);
+}
diff --git a/chkpw.3 b/chkpw.3
@@ -0,0 +1,36 @@
+.\" checkpassword-ng: Uniform password checking interface for applications
+.\" Copyright © 2021 checkpassword-ng Authors <https://hacktivis.me/git/checkpassword-ng>
+.\" SPDX-License-Identifier: GPL-3.0-only
+.Dd 2021-04-27
+.Dt CHKPW 3
+.Sh NAME
+.Lb chkpw
+.Nd Uniform password checking interface for applications
+.Sh SYNOPSIS
+.In chkpw.h
+.Vt extern struct chkpw_extra;
+.Ft char *
+.Fn chkpw "const char *username, const char *password, struct chkpw_extra"
+.Sh DESCRIPTION
+.Fn chkpw
+will verify
+.Aq password
+against
+.Aq username .
+Additionnal options might be passed in the future via
+.Aq chkpw_extra ,
+for now it is safe to give it
+.Aq NULL .
+.Pp
+This interface is for applications which already have access to files like
+.Pa /etc/shadow ,
+for others they should consider using
+.Xr checkpassword 8
+which doesn't requires your application to be running with special privileges.
+.Sh RETURN VALUES
+.Fn chkpw
+returns
+.Aq NULL
+on success, on failure it returns an error message to be passed to the user.
+.Sh Author
+.An Haelwenn (lanodan) Monnier Aq Mt contact+chkpw@hacktivis.me
diff --git a/chkpw.c b/chkpw.c
@@ -0,0 +1,92 @@
+// checkpassword-ng: Uniform password checking interface for applications
+// Copyright © 2021 checkpassword-ng Authors <https://hacktivis.me/git/checkpassword-ng>
+// SPDX-License-Identifier: GPL-3.0-only
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "chkpw.h"
+
+#include <assert.h> /* assert */
+#include <errno.h> /* errno */
+#include <pwd.h> /* getpwnam */
+#include <stdio.h> /* sprintf() */
+#include <string.h> /* strerror, strcmp */
+#include <unistd.h> /* crypt */
+#ifdef __GLIBC__
+// GNU's Not POSIX
+#include <crypt.h>
+#endif
+#ifdef __linux__
+// I love linux extensions (no)
+#include <shadow.h> /* getspnam */
+#endif
+
+char strerror_buf[1024];
+
+struct chkpw_extra
+{
+ // intentionally left blank
+} chkpw_extra;
+
+static void
+safe_strerror(char *desc, int errnum)
+{
+ char *err = strerror(errnum);
+
+ snprintf(strerror_buf, 1024, "%s: %s", desc, err);
+}
+
+char *
+chkpw(const char *username, const char *password, struct chkpw_extra *chkpw_extra)
+{
+ (void)chkpw_extra;
+ char *pw_hash = "";
+
+ struct passwd *pwent = getpwnam(username);
+ if(!pwent)
+ {
+ safe_strerror("getpwnam", errno);
+ return strerror_buf;
+ }
+
+ assert(pwent->pw_passwd);
+ pw_hash = pwent->pw_passwd;
+
+ if(strcmp(pw_hash, "") == 0)
+ {
+ return CHKPW_EMPTY;
+ }
+
+ if(strcmp(pw_hash, "x") == 0)
+ {
+#ifdef __linux__
+ struct spwd *swent = getspnam(username);
+ if(!swent)
+ {
+ safe_strerror("getspnam", errno);
+ return strerror_buf;
+ }
+ pw_hash = swent->sp_pwdp;
+#else /* __linux__ */
+ return "Invalid password entry";
+#endif
+ }
+
+ char *chk_hash = crypt(password, pw_hash);
+ if(chk_hash == NULL)
+ {
+ safe_strerror("crypt", errno);
+ return strerror_buf;
+ }
+
+ if(strcmp(chk_hash, pw_hash) == 0)
+ {
+ return CHKPW_VALID;
+ }
+ else
+ {
+ return CHKPW_INVALID;
+ }
+
+ assert(1);
+}
diff --git a/chkpw.h b/chkpw.h
@@ -0,0 +1,6 @@
+#define CHKPW_VALID NULL
+#define CHKPW_INVALID "Invalid password"
+#define CHKPW_EMPTY "Empty password entry"
+
+extern struct chkpw_extra chkpw_extra;
+char *chkpw(const char *username, const char *password, struct chkpw_extra *chkpw_extra);