commit 1ec996439ab06930041afb534a230cd8dd3d95ee
parent d3709f91a2028ffdee6721c96b5d082f696bddb9
Author: sin <sin@2f30.org>
Date: Thu, 5 Jun 2014 12:48:45 +0100
Add initial implementation of passwd(1)
No shadow support yet.
Diffstat:
8 files changed, 290 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,15 +3,17 @@ include config.mk
.POSIX:
.SUFFIXES: .c .o
-HDR = arg.h config.def.h proc.h reboot.h rtc.h util.h
+HDR = arg.h config.def.h passwd.h proc.h reboot.h rtc.h text.h util.h
LIB = \
util/agetcwd.o \
+ util/agetline.o \
util/apathmax.o \
util/ealloc.o \
util/eprintf.o \
util/estrtol.o \
util/estrtoul.o \
util/explicit_bzero.o \
+ util/passwd.o \
util/proc.o \
util/putword.o \
util/recurse.o \
@@ -43,6 +45,7 @@ SRC = \
mount.c \
mountpoint.c \
pagesize.c \
+ passwd.c \
pidof.c \
pivot_root.c \
ps.c \
diff --git a/README b/README
@@ -8,9 +8,9 @@ The following programs are currently implemented:
chvt clear ctrlaltdel dd df dmesg eject fallocate free getty halt
hwclock id insmod killall5 login lsmod lsusb mknod mkswap mount
- mountpoint pagesize pidof pivot_root ps respawn rmmod stat su
- swapoff swapon switch_root sysctl truncate umount unshare uptime
- watch who
+ mountpoint pagesize passwd pidof pivot_root ps respawn rmmod stat
+ su swapoff swapon switch_root sysctl truncate umount unshare
+ uptime watch who
The complement of ubase is sbase[1] which mostly follows POSIX and
provides all the portable tools. Together they are intended to form a
diff --git a/TODO b/TODO
@@ -9,7 +9,6 @@ Tools to be implemented
* losetup(8)
* lspci
* mkswap [-L]
- * passwd
* adduser
* addgroup
* rmuser
diff --git a/passwd.c b/passwd.c
@@ -0,0 +1,142 @@
+/* See LICENSE file for copyright and license details. */
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "passwd.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+ eprintf("usage: %s login\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *pass, *cryptpass1, *cryptpass2, *cryptpass3;
+ char *p;
+ char template[] = "/tmp/pw.XXXXXX";
+ struct passwd *pw;
+ int ffd, tfd;
+ int r;
+
+ ARGBEGIN {
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc != 1)
+ usage();
+
+ errno = 0;
+ pw = getpwnam(argv[0]);
+ if (errno)
+ eprintf("getpwnam: %s:", argv[0]);
+ else if (!pw)
+ eprintf("who are you?\n");
+
+ switch (pw->pw_passwd[0]) {
+ case '!':
+ case '*':
+ eprintf("denied\n");
+ }
+
+ if (pw->pw_passwd[0] == '\0')
+ goto newpass;
+
+ if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0')
+ eprintf("no shadow support\n");
+
+ /* Flush pending input */
+ ioctl(STDIN_FILENO, TCFLSH, (void *)0);
+
+ pass = getpass("Current password: ");
+ putchar('\n');
+ if (!pass)
+ eprintf("getpass:");
+ p = crypt(pass, pw->pw_passwd);
+ if (!p)
+ eprintf("crypt:");
+ cryptpass1 = estrdup(p);
+ if (strcmp(cryptpass1, pw->pw_passwd) != 0)
+ eprintf("incorrect password\n");
+
+newpass:
+ /* Flush pending input */
+ ioctl(STDIN_FILENO, TCFLSH, (void *)0);
+
+ pass = getpass("Enter new password: ");
+ putchar('\n');
+ if (!pass)
+ eprintf("getpass:");
+ p = crypt(pass, pw->pw_passwd);
+ if (!p)
+ eprintf("crypt:");
+ cryptpass2 = estrdup(p);
+ if (strcmp(cryptpass1, cryptpass2) == 0)
+ eprintf("password left unchanged\n");
+
+ /* Flush pending input */
+ ioctl(STDIN_FILENO, TCFLSH, (void *)0);
+
+ pass = getpass("Retype new password: ");
+ putchar('\n');
+ if (!pass)
+ eprintf("getpass:");
+ p = crypt(pass, pw->pw_passwd);
+ if (!p)
+ eprintf("crypt:");
+ cryptpass3 = estrdup(p);
+ if (strcmp(cryptpass2, cryptpass3) != 0)
+ eprintf("passwords don't match\n");
+
+ pw->pw_passwd = cryptpass3;
+
+ ffd = open("/etc/passwd", O_RDWR);
+ if (ffd < 0)
+ eprintf("open %s:", "/etc/passwd");
+
+ tfd = mkostemp(template, O_RDWR);
+ if (tfd < 0)
+ eprintf("mkstemp:");
+
+ r = pw_copy(ffd, tfd, pw);
+ if (r < 0) {
+ unlink(template);
+ exit(EXIT_FAILURE);
+ }
+
+ r = lseek(ffd, 0, SEEK_SET);
+ if (r < 0) {
+ unlink(template);
+ exit(EXIT_FAILURE);
+ }
+ r = lseek(tfd, 0, SEEK_SET);
+ if (r < 0) {
+ unlink(template);
+ exit(EXIT_FAILURE);
+ }
+
+ r = pw_copy(tfd, ffd, NULL);
+ if (r < 0) {
+ unlink(template);
+ exit(EXIT_FAILURE);
+ }
+
+ close(tfd);
+ close(ffd);
+ unlink(template);
+ free(cryptpass3);
+ free(cryptpass2);
+ free(cryptpass1);
+
+ return EXIT_SUCCESS;
+}
diff --git a/passwd.h b/passwd.h
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+/* passwd.c */
+int pw_scan(char *, struct passwd *);
+int pw_copy(int, int, const struct passwd *);
diff --git a/text.h b/text.h
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+
+struct linebuf {
+ char **lines;
+ long nlines;
+ long capacity;
+};
+#define EMPTY_LINEBUF {NULL, 0, 0,}
+void getlines(FILE *, struct linebuf *);
+
+ssize_t agetline(char **, size_t *, FILE *);
diff --git a/util/agetline.c b/util/agetline.c
@@ -0,0 +1,13 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../text.h"
+#include "../util.h"
+
+ssize_t
+agetline(char **p, size_t *size, FILE *fp)
+{
+ return getline(p, size, fp);
+}
diff --git a/util/passwd.c b/util/passwd.c
@@ -0,0 +1,113 @@
+/* See LICENSE file for copyright and license details. */
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../text.h"
+#include "../util.h"
+
+int
+pw_scan(char *bp, struct passwd *pw)
+{
+ char *p;
+
+ memset(pw, 0, sizeof(*pw));
+
+ /* login name */
+ p = strsep(&bp, ":");
+ if (!p || *p == '\0')
+ goto corrupt;
+ pw->pw_name = p;
+
+ /* passwd */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_passwd = p;
+
+ /* uid */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_uid = estrtol(p, 10);
+
+ /* gid */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_gid = estrtol(p, 10);
+
+ /* user name or comment */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_gecos = p;
+
+ /* home directory */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_dir = p;
+
+ /* optional shell */
+ p = strsep(&bp, ":");
+ if (!p)
+ goto corrupt;
+ pw->pw_shell = p;
+
+ /* look for redundant entries */
+ p = strsep(&bp, ":");
+ if (p)
+ goto corrupt;
+
+ return 0;
+corrupt:
+ weprintf("corrupted passwd entry\n");
+ return -1;
+}
+
+int
+pw_copy(int ffd, int tfd, const struct passwd *newpw)
+{
+ struct passwd pw;
+ char *buf = NULL, *p;
+ size_t size = 0;
+ FILE *from, *to;
+
+ from = fdopen(ffd, "r");
+ if (!from) {
+ weprintf("fdopen:");
+ return -1;
+ }
+ to = fdopen(tfd, "w");
+ if (!to) {
+ weprintf("fdopen:");
+ return -1;
+ }
+ while (agetline(&buf, &size, from) != -1) {
+ p = strdup(buf);
+ if (!p) {
+ weprintf("strdup:");
+ return -1;
+ }
+ if (newpw) {
+ if (pw_scan(p, &pw) < 0)
+ return -1;
+ if (strcmp(pw.pw_name, newpw->pw_name) == 0) {
+ fprintf(to, "%s:%s:%u:%u:%s:%s:%s\n",
+ newpw->pw_name,
+ newpw->pw_passwd,
+ newpw->pw_uid,
+ newpw->pw_gid,
+ newpw->pw_gecos,
+ newpw->pw_dir,
+ newpw->pw_shell);
+ continue;
+ }
+ }
+ fprintf(to, "%s", buf);
+ free(p);
+ }
+ fflush(to);
+ return 0;
+}