summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libshouldbeinlibc/idvec-verify.c196
1 files changed, 145 insertions, 51 deletions
diff --git a/libshouldbeinlibc/idvec-verify.c b/libshouldbeinlibc/idvec-verify.c
index fe4eb1d1..7cd8b9a5 100644
--- a/libshouldbeinlibc/idvec-verify.c
+++ b/libshouldbeinlibc/idvec-verify.c
@@ -22,6 +22,7 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <idvec.h>
#include <grp.h>
#include <pwd.h>
@@ -31,18 +32,104 @@ extern char *crypt (const char *string, const char salt[2]);
static error_t verify_id (); /* FWD */
+/* Get a password from the user, returning it in malloced storage. */
+static char *
+get_passwd (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+{
+ char *st = getpass (prompt);
+ if (st)
+ st = strdup (st);
+ return st;
+}
+
+/* Verify PASSWORD using /etc/passwd. */
+static error_t
+verify_passwd (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook)
+{
+ const char *encrypted;
+ int wheel_uid = (int)hook;
+ const char *sys_encrypted;
+
+ if (! pwd_or_grp)
+ /* No password db entry for ID; if ID is root, the system is probably
+ really fucked up, so grant it (heh). */
+ return (id == 0 ? 0 : EACCES);
+
+ /* The encrypted password in the passwd db. */
+ sys_encrypted =
+ (is_group
+ ? ((struct passwd *)pwd_or_grp)->pw_passwd
+ : ((struct group *)pwd_or_grp)->gr_passwd);
+
+ if (crypt)
+ /* Encrypt the password entered by the user (SYS_ENCRYPTED is the salt). */
+ encrypted = crypt (password, sys_encrypted);
+ else
+ /* No crypt on this system! Use plain-text passwords. */
+ encrypted = password;
+
+ if (! encrypted)
+ /* Crypt failed. */
+ return errno;
+
+ /* See whether the user's password matches the system one. */
+ if (strcmp (encrypted, sys_encrypted) == 0)
+ /* Password check succeeded. */
+ return 0;
+ else if (id == 0 && !is_group && wheel_uid)
+ /* Special hack: a user attempting to gain root access can use
+ their own password (instead of root's) if they're in group 0. */
+ {
+ struct passwd *pw = getpwuid (wheel_uid);
+
+ if (! pw)
+ return errno ?: EINVAL;
+
+ encrypted = crypt (password, pw->pw_passwd);
+ if (! encrypted)
+ /* Crypt failed. */
+ return errno;
+
+ if (strcmp (encrypted, pw->pw_passwd) == 0)
+ /* *this* password is correct! */
+ return 0;
+ }
+
+ return EACCES;
+}
+
/* Make sure the user has the right to the ids in UIDS and GIDS, given that
we know he already has HAVE_UIDS and HAVE_GIDS, asking for passwords (with
- GETPASS, which defaults to the standard libc function getpass) where
- necessary; any of the arguments may be 0, which is treated the same as if
- they were empty. 0 is returned if access should be allowed, otherwise
- EINVAL if an incorrect password was entered, or an error relating to
- resource failure. Any uid/gid < 0 will be guaranteed to fail regardless
- of what the user types. */
+ GETPASS_FN) where necessary; any of the arguments may be 0, which is
+ treated the same as if they were empty. 0 is returned if access should be
+ allowed, otherwise EINVAL if an incorrect password was entered, or an
+ error relating to resource failure. Any uid/gid < 0 will be guaranteed to
+ fail regardless of what the user types. GETPASS_FN should ask for a
+ password from the user, and return it in malloced storage; it defaults to
+ using the standard libc function getpass. If VERIFY_FN is 0, then the
+ users password will be encrypted with crypt and compared with the
+ password/group entry's encrypted password, otherwise, VERIFY_FN will be
+ called to check the entered password's validity; it should return 0 if the
+ given password is correct, or an error code. The common arguments to
+ GETPASS_FN and VERIFY_FN are: ID, the user/group id; IS_GROUP, true if its
+ a group, or false if a user; PWD_OR_GRP, a pointer to either the passwd or
+ group entry for ID, and HOOK, containing the appropriate hook passed into
+ idvec_verify. */
error_t
idvec_verify (const struct idvec *uids, const struct idvec *gids,
const struct idvec *have_uids, const struct idvec *have_gids,
- char *(*getpass_fn)(const char *prompt))
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook)
{
if (have_uids && idvec_contains (have_uids, 0))
/* Root can do anything. */
@@ -61,6 +148,12 @@ idvec_verify (const struct idvec *uids, const struct idvec *gids,
? have_uids->ids[0]
: 0);
+ if (! verify_fn)
+ {
+ verify_fn = verify_passwd;
+ verify_hook = (void *)wheel_uid;
+ }
+
/* See if there are multiple ids in contention, in which case we should
name each user/group as we ask for its password. */
if (uids && gids)
@@ -87,7 +180,8 @@ idvec_verify (const struct idvec *uids, const struct idvec *gids,
if (uids && idvec_contains (uids, 0))
/* root is being asked for, which, once granted will provide access for
all the others. */
- err = verify_id (0, 0, multiple, wheel_uid, getpass_fn);
+ err = verify_id (0, 0, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
else
{
if (uids)
@@ -96,7 +190,8 @@ idvec_verify (const struct idvec *uids, const struct idvec *gids,
{
uid_t uid = uids->ids[i];
if (!have_uids || !idvec_contains (have_uids, uid))
- err = verify_id (uid, 0, multiple, wheel_uid, getpass_fn);
+ err = verify_id (uid, 0, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
}
if (gids)
@@ -106,7 +201,8 @@ idvec_verify (const struct idvec *uids, const struct idvec *gids,
gid_t gid = gids->ids[i];
if ((!have_gids || !idvec_contains (have_gids, gid))
&& !idvec_contains (&implied_gids, gid))
- err = verify_id (gid, 1, multiple, wheel_uid, getpass_fn);
+ err = verify_id (gid, 1, multiple,
+ getpass_fn, getpass_hook, verify_fn, verify_hook);
}
}
@@ -120,12 +216,20 @@ idvec_verify (const struct idvec *uids, const struct idvec *gids,
user/group ID (depending on whether IS_GROUP is false/true). If MULTIPLE
is true, then this is one of multiple ids being verified, so */
static error_t
-verify_id (uid_t id, int is_group, int multiple, int wheel_uid,
- char *(*getpass_fn)(const char *prompt))
+verify_id (uid_t id, int is_group, int multiple,
+ char *(*getpass_fn) (const char *prompt,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *getpass_hook,
+ error_t (*verify_fn) (const char *password,
+ uid_t id, int is_group,
+ void *pwd_or_grp, void *hook),
+ void *verify_hook)
{
int err;
- char *name = 0, *password = 0;
- char *prompt = 0, *unencrypted, *encrypted;
+ void *pwd_or_grp = 0;
+ char *name = 0;
+ char *prompt = 0, *password;
char id_lookup_buf[1024];
if (id >= 0)
@@ -137,8 +241,10 @@ verify_id (uid_t id, int is_group, int multiple, int wheel_uid,
if (getgrgid_r (id, &_gr, id_lookup_buf, sizeof id_lookup_buf, &gr)
== 0)
{
- password = gr->gr_passwd;
+ if (!gr->gr_passwd || !*gr->gr_passwd)
+ return 0; /* No password. */
name = gr->gr_name;
+ pwd_or_grp = gr;
}
}
else
@@ -147,8 +253,10 @@ verify_id (uid_t id, int is_group, int multiple, int wheel_uid,
if (getpwuid_r (id, &_pw, id_lookup_buf, sizeof id_lookup_buf, &pw)
== 0)
{
- password = pw->pw_passwd;
+ if (!pw->pw_passwd || !*pw->pw_passwd)
+ return 0; /* No password. */
name = pw->pw_name;
+ pwd_or_grp = pw;
}
}
if (! name)
@@ -163,21 +271,17 @@ verify_id (uid_t id, int is_group, int multiple, int wheel_uid,
multiple = 1; /* Explicitly ask for root's password. */
}
else
- /* If ID == 0 && !IS_GROUP, then this means that the system is
- really fucked up (there's no root password entry), so instead
- just don't ask for a password at all (if an intruder has
- succeeded in preventing the lookup somehow, he probably could
- have just provided his own result anyway). */
- name = "uh-oh";
+ /* No password entry for root. */
+ name = "root";
}
while (! name);
- if (!password || !*password)
- /* No password! */
- return 0;
-
if (! getpass_fn)
- getpass_fn = getpass;
+ /* Default GETPASS_FN to using getpass. */
+ getpass_fn = get_passwd;
+
+ /* VERIFY_FN should have been defaulted in idvec_verify if necessary. */
+ assert (verify_fn);
if (multiple)
if (name)
@@ -186,40 +290,30 @@ verify_id (uid_t id, int is_group, int multiple, int wheel_uid,
else
asprintf (&prompt, "Password for %s %d:",
is_group ? "group" : "user", id);
+
+ /* Prompt the user for the password. */
if (prompt)
{
- unencrypted = (*getpass_fn) (prompt);
+ password =
+ (*getpass_fn) (prompt, id, is_group, pwd_or_grp, getpass_hook);
free (prompt);
}
else
- unencrypted = (*getpass_fn) ("Password:");
+ password =
+ (*getpass_fn) ("Password:", id, is_group, pwd_or_grp, getpass_hook);
- if (crypt)
+ /* Check the user's answer. */
+ if (password)
{
- encrypted = crypt (unencrypted, password);
- if (! encrypted)
- /* Something went wrong. */
- return errno;
- }
- else
- encrypted = unencrypted;
+ err = (*verify_fn) (password, id, is_group, pwd_or_grp, verify_hook);
- err = EINVAL; /* Assume an invalid password. */
+ /* Paranoia may destroya. */
+ memset (password, 0, strlen (password));
- if (encrypted && strcmp (encrypted, password) == 0)
- err = 0; /* Password correct! */
- else if (id == 0 && !is_group && wheel_uid)
- /* Special hack: a user attempting to gain root access can use
- their own password (instead of root's) if they're in group 0. */
- {
- struct passwd *pw = getpwuid (wheel_uid);
- encrypted = crypt (unencrypted, pw->pw_passwd);
- if (pw && encrypted && strcmp (encrypted, pw->pw_passwd) == 0)
- err = 0; /* *this* password is correct! */
+ free (password);
}
-
- /* Paranoia may destroya. */
- memset (unencrypted, 0, strlen (unencrypted));
+ else
+ err = EACCES;
return err;
}