/* Test userspec.c
Copyright (C) 2009-2023 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Written by Jim Meyering. */
#include
#include "userspec.h"
#include
#include
#include
#include
#include
#include
#include
#include "xalloc.h"
#define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
struct test
{
char in[100];
uid_t uid;
gid_t gid;
const char *user_name;
const char *group_name;
const char *result;
};
static struct test T[] =
{
{ "", -1, -1, "", "", NULL},
{ ":", -1, -1, "", "", NULL},
{ "+0:+0", 0, 0, "", "", NULL},
{ "+0:+0", 0, 0, "", "", NULL},
{ ":+1", -1, 1, "", "", NULL},
{ "+1", 1, -1, "", "", NULL},
{ ":+0", -1, 0, "", "", NULL},
{ "+22:+42", 22, 42, "", "", NULL},
/* (uint32_t)-1 should be invalid everywhere */
{ "+4294967295:+4294967295", 0, 0, NULL, NULL, "invalid user"},
/* likewise, but with only the group being invalid */
{ "+0:+4294967295", 0, 0, NULL, NULL, "invalid group"},
{ ":+4294967295", 0, 0, NULL, NULL, "invalid group"},
/* and only the user being invalid */
{ "+4294967295:+0", 0, 0, NULL, NULL, "invalid user"},
/* and using 2^32 */
{ "+4294967296:+4294967296", 0, 0, NULL, NULL, "invalid user"},
{ "+0:+4294967296", 0, 0, NULL, NULL, "invalid group"},
{ ":+4294967296", 0, 0, NULL, NULL, "invalid group"},
{ "+4294967296:+0", 0, 0, NULL, NULL, "invalid user"},
/* numeric user and no group is invalid */
{ "+4294967295:", 0, 0, NULL, NULL, "invalid spec"},
{ "+4294967296:", 0, 0, NULL, NULL, "invalid spec"},
{ "+1:", 0, 0, NULL, NULL, "invalid spec"},
{ "+0:", 0, 0, NULL, NULL, "invalid spec"},
/* "username:" must expand to UID:GID where GID is username's login group */
/* Add an entry like the following to the table, if possible.
{ "U_NAME:", UID,GID, U_NAME, G_NAME, NULL}, */
{ "" /* placeholder */, -1, -1, "", "", NULL},
};
#define STREQ(a, b) (strcmp (a, b) == 0)
static char const *
maybe_null (char const *s)
{
return s ? s : "NULL";
}
static bool
same_diag (char const *s, char const *t)
{
if (s == NULL && t == NULL)
return true;
if (s == NULL || t == NULL)
return false;
return STREQ (s, t);
}
int
main (void)
{
unsigned int i;
int fail = 0;
/* Find a UID that has both a user name and login group name,
but skip UID 0. */
{
uid_t uid;
for (uid = 1200; 0 < uid; uid--)
{
struct group *gr;
struct passwd *pw = getpwuid (uid);
unsigned int j;
size_t len;
if (!pw || !pw->pw_name || !(gr = getgrgid (pw->pw_gid)) || !gr->gr_name)
continue;
j = ARRAY_CARDINALITY (T) - 1;
len = strlen (pw->pw_name);
if (sizeof T[j].in - 2 < len)
continue;
/* Store "username:" in T[j].in. */
memcpy(T[j].in, pw->pw_name, len);
strcpy(T[j].in + len, ":");
T[j].uid = uid;
T[j].gid = gr->gr_gid;
T[j].user_name = xstrdup (pw->pw_name);
T[j].group_name = xstrdup (gr->gr_name);
T[j].result = NULL;
break;
}
}
char *user_name = NULL;
char *group_name = NULL;
for (i = 0; i < ARRAY_CARDINALITY (T); i++)
{
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
free (user_name);
free (group_name);
char const *diag = parse_user_spec (T[i].in, &uid, &gid,
&user_name, &group_name);
if (!same_diag (diag, T[i].result))
{
printf ("%s return value mismatch: got %s, expected %s\n",
T[i].in, maybe_null (diag), maybe_null (T[i].result));
fail = 1;
continue;
}
if (diag)
continue;
if (uid != T[i].uid || gid != T[i].gid)
{
printf ("%s mismatch (-: expected uid,gid; +:actual)\n"
"-%3lu,%3lu\n+%3lu,%3lu\n",
T[i].in,
(unsigned long int) T[i].uid,
(unsigned long int) T[i].gid,
(unsigned long int) uid,
(unsigned long int) gid);
fail = 1;
}
if (T[i].result)
{
/* Expected a non-NULL result diagnostic, yet got NULL. */
diag = "NULL";
printf ("%s diagnostic mismatch (-: expected diagnostic; +:actual)\n"
"-%s\n+%s\n", T[i].in, T[i].result, diag);
fail = 1;
}
else
{
/* Should get the same result, but with a warning, if replacing
':' with '.'. */
char *colon = strchr (T[i].in, ':');
if (colon)
{
*colon = '.';
uid_t uid2 = -1;
gid_t gid2 = -1;
char *user_name2 = NULL;
char *group_name2 = NULL;
bool warn;
if (! (parse_user_spec_warn (T[i].in, &uid2, &gid2,
&user_name2, &group_name2, &warn)
&& warn))
printf ("%s did not warn\n", T[i].in);
else if (! (uid == uid2 && gid == gid2
&& !!user_name == !!user_name2
&& (!user_name || STREQ (user_name, user_name2))
&& !!group_name == !!group_name2
&& (!group_name || STREQ (group_name, group_name2))))
printf ("%s treated differently than with colon\n", T[i].in);
free (user_name2);
free (group_name2);
}
}
}
/* Ensure NULL parameters are ignored. */
{
uid_t uid = (uid_t) -1;
char const *diag = parse_user_spec ("", &uid, NULL, NULL, NULL);
if (diag)
{
printf ("unexpected error: %s\n", diag);
fail = 1;
}
}
return fail;
}
/*
Local Variables:
indent-tabs-mode: nil
End:
*/