* 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:
@@ -181,6 +181,7 @@ SubInclude HAIKU_TOP src bin make ;
|
||||
SubInclude HAIKU_TOP src bin makebootable ;
|
||||
#SubInclude HAIKU_TOP src bin makeudfimage ;
|
||||
SubInclude HAIKU_TOP src bin mkdos ;
|
||||
SubInclude HAIKU_TOP src bin multiuser ;
|
||||
SubInclude HAIKU_TOP src bin patch ;
|
||||
SubInclude HAIKU_TOP src bin pc ;
|
||||
SubInclude HAIKU_TOP src bin pcmcia-cs ;
|
||||
|
||||
@@ -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 <unistd.h>
|
||||
|
||||
#include "multiuser_utils.h"
|
||||
|
||||
|
||||
extern const char* __progname;
|
||||
const char* kProgramName = __progname;
|
||||
@@ -118,11 +120,12 @@ login(const char* user, struct passwd** _passwd)
|
||||
return status;
|
||||
|
||||
struct passwd* passwd = getpwnam(user);
|
||||
if (passwd == NULL || passwd->pw_passwd == NULL)
|
||||
return B_ERROR;
|
||||
struct spwd* spwd = getspnam(user);
|
||||
|
||||
// TODO: do a real password check!
|
||||
if (strcmp(password, passwd->pw_passwd))
|
||||
bool ok = verify_password(passwd, spwd, password);
|
||||
memset(password, 0, sizeof(password));
|
||||
|
||||
if (!ok)
|
||||
return B_PERMISSION_DENIED;
|
||||
|
||||
*_passwd = passwd;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -5,7 +5,6 @@ SubInclude HAIKU_TOP src bin network atftpd ;
|
||||
SubInclude HAIKU_TOP src bin network ftp ;
|
||||
SubInclude HAIKU_TOP src bin network ftpd ;
|
||||
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 nc ;
|
||||
SubInclude HAIKU_TOP src bin network netstat ;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
SubDir HAIKU_TOP src bin network login ;
|
||||
|
||||
BinCommand login :
|
||||
login.cpp
|
||||
;
|
||||
Reference in New Issue
Block a user