* Added useradd and passwd tools. They allow adding new users and

changing a user's password, like on other platforms (just a bit more
  bare-bones :-)).
* Moved login from src/bin/network/login/ to src/bin/multiuser/.
* login correctly checks the shadow password now.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25041 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-04-19 00:15:59 +00:00
parent f694f63e2b
commit f5e8e68924
9 changed files with 555 additions and 10 deletions
+1
View File
@@ -181,6 +181,7 @@ SubInclude HAIKU_TOP src bin make ;
SubInclude HAIKU_TOP src bin makebootable ; SubInclude HAIKU_TOP src bin makebootable ;
#SubInclude HAIKU_TOP src bin makeudfimage ; #SubInclude HAIKU_TOP src bin makeudfimage ;
SubInclude HAIKU_TOP src bin mkdos ; SubInclude HAIKU_TOP src bin mkdos ;
SubInclude HAIKU_TOP src bin multiuser ;
SubInclude HAIKU_TOP src bin patch ; SubInclude HAIKU_TOP src bin patch ;
SubInclude HAIKU_TOP src bin pc ; SubInclude HAIKU_TOP src bin pc ;
SubInclude HAIKU_TOP src bin pcmcia-cs ; SubInclude HAIKU_TOP src bin pcmcia-cs ;
+17
View File
@@ -0,0 +1,17 @@
SubDir HAIKU_TOP src bin multiuser ;
UsePrivateKernelHeaders ;
UsePrivateHeaders app ;
UsePrivateHeaders libroot ;
UsePrivateHeaders shared ;
StaticLibrary libmultiuser_utils.a : multiuser_utils.cpp ;
BinCommand login : login.cpp : libmultiuser_utils.a ;
BinCommand passwd : passwd.cpp : libmultiuser_utils.a ;
BinCommand useradd : useradd.cpp : libmultiuser_utils.a ;
# set set-uid bit on passwd
MODE on passwd = 04755 ;
@@ -19,6 +19,8 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
#include "multiuser_utils.h"
extern const char* __progname; extern const char* __progname;
const char* kProgramName = __progname; const char* kProgramName = __progname;
@@ -118,11 +120,12 @@ login(const char* user, struct passwd** _passwd)
return status; return status;
struct passwd* passwd = getpwnam(user); struct passwd* passwd = getpwnam(user);
if (passwd == NULL || passwd->pw_passwd == NULL) struct spwd* spwd = getspnam(user);
return B_ERROR;
// TODO: do a real password check! bool ok = verify_password(passwd, spwd, password);
if (strcmp(password, passwd->pw_passwd)) memset(password, 0, sizeof(password));
if (!ok)
return B_PERMISSION_DENIED; return B_PERMISSION_DENIED;
*_passwd = passwd; *_passwd = passwd;
+167
View File
@@ -0,0 +1,167 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected]. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include "multiuser_utils.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <AutoDeleter.h>
#include <user_group.h>
status_t
read_password(const char* prompt, char* password, size_t bufferSize,
bool useStdio)
{
FILE* in = stdin;
FILE* out = stdout;
// open tty
FILE* tty = NULL;
if (!useStdio) {
// TODO: Open tty with O_NOCTTY!
tty = fopen("/dev/tty", "w+");
if (tty == NULL) {
fprintf(stderr, "Error: Failed to open tty: %s\n", strerror(errno));
return errno;
}
in = tty;
out = tty;
}
CObjectDeleter<FILE, int> ttyCloser(tty, fclose);
// disable echo
int inFD = fileno(in);
struct termios termAttrs;
if (tcgetattr(inFD, &termAttrs) != 0) {
fprintf(in, "Error: Failed to get tty attributes: %s\n",
strerror(errno));
return errno;
}
tcflag_t localFlags = termAttrs.c_lflag;
termAttrs.c_lflag &= ~ECHO;
if (tcsetattr(inFD, TCSANOW, &termAttrs) != 0) {
fprintf(in, "Error: Failed to set tty attributes: %s\n",
strerror(errno));
return errno;
}
status_t error = B_OK;
// prompt and read pwd
fprintf(out, prompt);
fflush(out);
if (fgets(password, bufferSize, in) == NULL) {
fprintf(out, "\nError: Failed to read from tty: %s\n", strerror(errno));
error = errno != 0 ? errno : B_ERROR;
} else
fputc('\n', out);
// chop off trailing newline
if (error == B_OK) {
size_t len = strlen(password);
if (len > 0 && password[len - 1] == '\n')
password[len - 1] = '\0';
}
// restore the terminal attributes
termAttrs.c_lflag = localFlags;
tcsetattr(inFD, TCSANOW, &termAttrs);
return error;
}
bool
verify_password(passwd* passwd, spwd* spwd, const char* plainPassword)
{
if (passwd == NULL)
return false;
// check whether we need to check the shadow password
const char* requiredPassword = passwd->pw_passwd;
if (strcmp(requiredPassword, "x") == 0) {
if (spwd == NULL) {
// Mmh, we're suppose to check the shadow password, but we don't
// have it. Bail out.
return false;
}
requiredPassword = spwd->sp_pwdp;
}
// If no password is required, we're done.
if (requiredPassword == NULL || strlen(requiredPassword) == 0)
return true;
// crypt and check it
char* encryptedPassword = crypt(plainPassword, requiredPassword);
return (strcmp(encryptedPassword, requiredPassword) == 0);
}
/*! Checks whether the user needs to authenticate with a password, and, if
necessary, asks for it, and checks it.
\a passwd must always be given, \a spwd only if there exists an entry
for the user.
*/
status_t
authenticate_user(const char* prompt, passwd* passwd, spwd* spwd, int maxTries,
bool useStdio)
{
// check whether a password is need at all
if (verify_password(passwd, spwd, ""))
return B_OK;
while (true) {
// prompt the user for the password
char plainPassword[MAX_SHADOW_PWD_PASSWORD_LEN];
status_t error = read_password(prompt, plainPassword,
sizeof(plainPassword), useStdio);
if (error != B_OK)
return error;
// check it
bool ok = verify_password(passwd, spwd, plainPassword);
memset(plainPassword, 0, sizeof(plainPassword));
if (ok)
return B_OK;
fprintf(stderr, "Incorrect password.\n");
if (--maxTries <= 0)
return B_PERMISSION_DENIED;
}
}
status_t
authenticate_user(const char* prompt, const char* user, passwd** _passwd,
spwd** _spwd, int maxTries, bool useStdio)
{
struct passwd* passwd = getpwnam(user);
struct spwd* spwd = getspnam(user);
status_t error = authenticate_user(prompt, passwd, spwd, maxTries,
useStdio);
if (error == B_OK) {
if (_passwd)
*_passwd = passwd;
if (_spwd)
*_spwd = spwd;
}
return error;
}
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected]. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef MULTIUSER_UTILS_H
#define MULTIUSER_UTILS_H
#include <pwd.h>
#include <shadow.h>
#include <stdio.h>
#include <SupportDefs.h>
status_t read_password(const char* prompt, char* password, size_t bufferSize,
bool useStdio);
bool verify_password(passwd* passwd, spwd* spwd, const char* plainPassword);
status_t authenticate_user(const char* prompt, passwd* passwd, spwd* spwd,
int maxTries, bool useStdio);
status_t authenticate_user(const char* prompt, const char* user,
passwd** _passwd, spwd** _spwd, int maxTries, bool useStdio);
#endif // MULTIUSER_UTILS_H
+145
View File
@@ -0,0 +1,145 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected]. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <pwd.h>
#include <shadow.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <OS.h>
#include <RegistrarDefs.h>
#include <user_group.h>
#include <util/KMessage.h>
#include <AutoDeleter.h>
#include "multiuser_utils.h"
extern const char *__progname;
static const char* kUsage =
"Usage: %s [ <user name> ]\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
int
main(int argc, const char* const* argv)
{
if (argc > 2)
print_usage_and_exit(true);
const char* user = NULL;
if (argc == 2)
user = argv[1];
if (geteuid() != 0) {
fprintf(stderr, "Error: You need to be root.\n");
exit(1);
}
// this is a set-uid tool -- get the real UID
uid_t uid = getuid();
// get the passwd entry
struct passwd* passwd;
if (user != NULL) {
passwd = getpwnam(user);
if (passwd == NULL) {
fprintf(stderr, "Error: No user with name \"%s\".\n", user);
exit(1);
}
if (uid != 0 && passwd->pw_uid != uid) {
fprintf(stderr, "Error: Only root can change the passwd for other "
"users.\n");
exit(1);
}
} else {
passwd = getpwuid(uid);
if (passwd == NULL) {
fprintf(stderr, "Error: Ugh! Couldn't get passwd entry for uid "
"%d.\n", uid);
exit(1);
}
user = passwd->pw_name;
}
// if not root, the user needs to authenticate
if (uid != 0) {
if (authenticate_user("old password: ", passwd, getspnam(user), 1,
false) != B_OK) {
exit(1);
}
}
// read new password
char password[LINE_MAX];
if (read_password("new password: ", password, sizeof(password), false)
!= B_OK) {
exit(1);
}
if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
fprintf(stderr, "Error: The password is too long.\n");
exit(1);
}
// read password again
char repeatedPassword[LINE_MAX];
if (read_password("repeat new password: ", repeatedPassword,
sizeof(repeatedPassword), false) != B_OK) {
exit(1);
}
// passwords need to match
if (strcmp(password, repeatedPassword) != 0) {
fprintf(stderr, "Error: passwords don't match\n");
exit(1);
}
memset(repeatedPassword, 0, sizeof(repeatedPassword));
// crypt it
char* encryptedPassword;
if (strlen(password) > 0) {
encryptedPassword = crypt(password, user);
memset(password, 0, sizeof(password));
} else
encryptedPassword = password;
// prepare request for the registrar
KMessage message(BPrivate::B_REG_UPDATE_USER);
if (message.AddInt32("uid", passwd->pw_uid) != B_OK
|| message.AddString("password", "x") != B_OK
|| message.AddString("shadow password", encryptedPassword) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
// send the request
KMessage reply;
status_t error = send_authentication_request_to_registrar(message, reply);
if (error != B_OK) {
fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error));
exit(1);
}
return 0;
}
+192
View File
@@ -0,0 +1,192 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected]. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <getopt.h>
#include <pwd.h>
#include <shadow.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <OS.h>
#include <parsedate.h>
#include <RegistrarDefs.h>
#include <user_group.h>
#include <util/KMessage.h>
#include <AutoDeleter.h>
#include "multiuser_utils.h"
extern const char *__progname;
static const char* kUsage =
"Usage: %s [ -d <home> ] [ -e <expiration> ] [ -f <inactive> ] [ -g <gid> ]\n"
" [ -s <shell> ] [ -n <real name> ]\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
int
main(int argc, const char* const* argv)
{
const char* home = "/boot/home";
int expiration = 99999;
int inactive = -1;
gid_t gid = 100;
const char* shell = "/bin/sh";
const char* realName = "";
int min = -1;
int max = -1;
int warn = 7;
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "d:e:f:g:hn:s:", sLongOptions,
NULL);
if (c == -1)
break;
switch (c) {
case 'd':
home = optarg;
break;
case 'e':
expiration = parsedate(optarg, time(NULL)) / (3600 * 24);
break;
case 'f':
inactive = atoi(optarg);
break;
case 'g':
gid = atoi(optarg);
break;
case 'h':
print_usage_and_exit(false);
break;
case 'n':
realName = optarg;
break;
case 's':
shell = optarg;
break;
default:
print_usage_and_exit(true);
break;
}
}
if (optind != argc - 1)
print_usage_and_exit(true);
const char* user = argv[optind];
if (geteuid() != 0) {
fprintf(stderr, "Error: You need to be root.\n");
exit(1);
}
// check, if user already exists
if (getpwnam(user) != NULL) {
fprintf(stderr, "Error: User \"%s\" already exists.\n", user);
exit(1);
}
// read password
char password[LINE_MAX];
if (read_password("password for user: ", password, sizeof(password),
false) != B_OK) {
exit(1);
}
if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
fprintf(stderr, "Error: The password is too long.\n");
exit(1);
}
// read password again
char repeatedPassword[LINE_MAX];
if (read_password("repeat password: ", repeatedPassword,
sizeof(repeatedPassword), false) != B_OK) {
exit(1);
}
// passwords need to match
if (strcmp(password, repeatedPassword) != 0) {
fprintf(stderr, "Error: passwords don't match\n");
exit(1);
}
memset(repeatedPassword, 0, sizeof(repeatedPassword));
// crypt it
char* encryptedPassword;
if (strlen(password) > 0) {
encryptedPassword = crypt(password, user);
memset(password, 0, sizeof(password));
} else
encryptedPassword = password;
// find an unused UID
uid_t uid = 1000;
while (getpwuid(uid) != NULL)
uid++;
// prepare request for the registrar
KMessage message(BPrivate::B_REG_UPDATE_USER);
if (message.AddInt32("uid", uid) != B_OK
|| message.AddInt32("gid", gid) != B_OK
|| message.AddString("name", user) != B_OK
|| message.AddString("password", "x") != B_OK
|| message.AddString("home", home) != B_OK
|| message.AddString("shell", shell) != B_OK
|| message.AddString("real name", realName) != B_OK
|| message.AddString("shadow password", encryptedPassword) != B_OK
|| message.AddInt32("min", min) != B_OK
|| message.AddInt32("max", max) != B_OK
|| message.AddInt32("warn", warn) != B_OK
|| message.AddInt32("inactive", inactive) != B_OK
|| message.AddInt32("expiration", expiration) != B_OK
|| message.AddInt32("flags", 0) != B_OK
|| message.AddBool("add user", true) != B_OK) {
fprintf(stderr, "Error: Out of memory!\n");
exit(1);
}
// send the request
KMessage reply;
status_t error = send_authentication_request_to_registrar(message, reply);
if (error != B_OK) {
fprintf(stderr, "Error: Failed to create user: %s\n", strerror(error));
exit(1);
}
return 0;
}
-1
View File
@@ -5,7 +5,6 @@ SubInclude HAIKU_TOP src bin network atftpd ;
SubInclude HAIKU_TOP src bin network ftp ; SubInclude HAIKU_TOP src bin network ftp ;
SubInclude HAIKU_TOP src bin network ftpd ; SubInclude HAIKU_TOP src bin network ftpd ;
SubInclude HAIKU_TOP src bin network ifconfig ; SubInclude HAIKU_TOP src bin network ifconfig ;
SubInclude HAIKU_TOP src bin network login ;
SubInclude HAIKU_TOP src bin network mount_nfs ; SubInclude HAIKU_TOP src bin network mount_nfs ;
SubInclude HAIKU_TOP src bin network nc ; SubInclude HAIKU_TOP src bin network nc ;
SubInclude HAIKU_TOP src bin network netstat ; SubInclude HAIKU_TOP src bin network netstat ;
-5
View File
@@ -1,5 +0,0 @@
SubDir HAIKU_TOP src bin network login ;
BinCommand login :
login.cpp
;