1 /* Test userspec.c
2    Copyright (C) 2009-2021 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Jim Meyering.  */
18 
19 #include <config.h>
20 
21 #include "userspec.h"
22 
23 #include <stdio.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <sys/types.h>
29 #include <pwd.h>
30 #include <grp.h>
31 
32 #include "xalloc.h"
33 
34 #define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
35 
36 struct test
37 {
38   const char *in;
39   uid_t uid;
40   gid_t gid;
41   const char *user_name;
42   const char *group_name;
43   const char *result;
44 };
45 
46 static struct test T[] =
47   {
48     { "",                       -1, -1, "",   "",   NULL},
49     { ":",                      -1, -1, "",   "",   NULL},
50     { "+0:+0",                   0,  0, "",   "",   NULL},
51     { ":+1",                    -1,  1, "",   "",   NULL},
52     { "+1",                      1, -1, "",   "",   NULL},
53     { ":+0",                    -1,  0, "",   "",   NULL},
54     { "+22:+42",                22, 42, "",   "",   NULL},
55     /* (uint32_t)-1 should be invalid everywhere */
56     { "+4294967295:+4294967295", 0,  0, NULL, NULL, "invalid user"},
57     /* likewise, but with only the group being invalid */
58     { "+0:+4294967295",          0,  0, NULL, NULL, "invalid group"},
59     { ":+4294967295",            0,  0, NULL, NULL, "invalid group"},
60     /* and only the user being invalid */
61     { "+4294967295:+0",          0,  0, NULL, NULL, "invalid user"},
62     /* and using 2^32 */
63     { "+4294967296:+4294967296", 0,  0, NULL, NULL, "invalid user"},
64     { "+0:+4294967296",          0,  0, NULL, NULL, "invalid group"},
65     { ":+4294967296",            0,  0, NULL, NULL, "invalid group"},
66     { "+4294967296:+0",          0,  0, NULL, NULL, "invalid user"},
67     /* numeric user and no group is invalid */
68     { "+4294967295:",            0,  0, NULL, NULL, "invalid spec"},
69     { "+4294967296:",            0,  0, NULL, NULL, "invalid spec"},
70     { "+1:",                     0,  0, NULL, NULL, "invalid spec"},
71     { "+0:",                     0,  0, NULL, NULL, "invalid spec"},
72 
73     /* "username:" must expand to UID:GID where GID is username's login group */
74     /* Add an entry like the following to the table, if possible.
75     { "U_NAME:",              UID,GID, U_NAME, G_NAME, NULL}, */
76     { NULL,                     0,  0, NULL, NULL, ""},  /* place-holder */
77 
78     { NULL,                     0,  0, NULL, NULL, ""}
79   };
80 
81 #define STREQ(a, b) (strcmp (a, b) == 0)
82 
83 static char const *
maybe_null(char const * s)84 maybe_null (char const *s)
85 {
86   return s ? s : "NULL";
87 }
88 
89 static bool
same_diag(char const * s,char const * t)90 same_diag (char const *s, char const *t)
91 {
92   if (s == NULL && t == NULL)
93     return true;
94   if (s == NULL || t == NULL)
95     return false;
96   return STREQ (s, t);
97 }
98 
99 int
main(void)100 main (void)
101 {
102   unsigned int i;
103   int fail = 0;
104 
105   /* Find a UID that has both a user name and login group name,
106      but skip UID 0.  */
107   {
108     uid_t uid;
109     for (uid = 1200; 0 < uid; uid--)
110       {
111         struct group *gr;
112         struct passwd *pw = getpwuid (uid);
113         unsigned int j;
114         size_t len;
115         if (!pw || !pw->pw_name || !(gr = getgrgid (pw->pw_gid)) || !gr->gr_name)
116           continue;
117         j = ARRAY_CARDINALITY (T) - 2;
118         assert (T[j].in == NULL);
119         assert (T[j+1].in == NULL);
120         len = strlen (pw->pw_name);
121 
122         /* Store "username:" in T[j].in.  */
123         {
124           char *t = xmalloc (len + 1 + 1);
125           memcpy (t, pw->pw_name, len);
126           t[len] = ':';
127           t[len+1] = '\0';
128           T[j].in = t;
129         }
130 
131         T[j].uid = uid;
132         T[j].gid = gr->gr_gid;
133         T[j].user_name = xstrdup (pw->pw_name);
134         T[j].group_name = xstrdup (gr->gr_name);
135         T[j].result = NULL;
136         break;
137       }
138   }
139 
140   for (i = 0; T[i].in; i++)
141     {
142       uid_t uid = (uid_t) -1;
143       gid_t gid = (gid_t) -1;
144       char *user_name;
145       char *group_name;
146       char const *diag = parse_user_spec (T[i].in, &uid, &gid,
147                                           &user_name, &group_name);
148       free (user_name);
149       free (group_name);
150       if (!same_diag (diag, T[i].result))
151         {
152           printf ("%s return value mismatch: got %s, expected %s\n",
153                   T[i].in, maybe_null (diag), maybe_null (T[i].result));
154           fail = 1;
155           continue;
156         }
157 
158       if (diag)
159         continue;
160 
161       if (uid != T[i].uid || gid != T[i].gid)
162         {
163           printf ("%s mismatch (-: expected uid,gid; +:actual)\n"
164                   "-%3lu,%3lu\n+%3lu,%3lu\n",
165                   T[i].in,
166                   (unsigned long int) T[i].uid,
167                   (unsigned long int) T[i].gid,
168                   (unsigned long int) uid,
169                   (unsigned long int) gid);
170           fail = 1;
171         }
172 
173       if (!T[i].result)
174         continue;
175 
176       /* Expected a non-NULL result diagnostic, yet got NULL.  */
177       diag = "NULL";
178       printf ("%s diagnostic mismatch (-: expected diagnostic; +:actual)\n"
179               "-%s\n+%s\n", T[i].in, T[i].result, diag);
180       fail = 1;
181     }
182 
183   /* Ensure NULL parameters are ignored.  */
184   {
185     uid_t uid = (uid_t) -1;
186     char const *diag = parse_user_spec ("", &uid, NULL, NULL, NULL);
187     if (diag)
188       {
189         printf ("unexpected error: %s\n", diag);
190         fail = 1;
191       }
192   }
193 
194   return fail;
195 }
196 
197 /*
198 Local Variables:
199 indent-tabs-mode: nil
200 End:
201 */
202