Skip to content

Commit f6e5f2a

Browse files
testing/ostest: expand multiuser coverage for groups and setresuid
Add supplementary-group, setresuid/setresgid ordering, and saved set-UID/GID child tests for the POSIX credential APIs. Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
1 parent 569d8f3 commit f6e5f2a

1 file changed

Lines changed: 208 additions & 4 deletions

File tree

testing/ostest/multiuser.c

Lines changed: 208 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
#include <stdarg.h>
3030
#include <errno.h>
3131
#include <fcntl.h>
32+
#include <limits.h>
33+
#include <grp.h>
3234
#include <pwd.h>
35+
#include <stdint.h>
3336
#include <stdio.h>
3437
#include <stdlib.h>
3538
#include <string.h>
@@ -148,9 +151,9 @@ static int mu_restore_root(FAR struct mu_ctx_s *ctx)
148151

149152
static int mu_set_effective(FAR struct mu_ctx_s *ctx, uid_t uid, gid_t gid)
150153
{
151-
/* NuttX grants arbitrary seteuid/setegid only while the effective ID
152-
* is 0. With real UID 0 (flat NSH), restore effective root before
153-
* switching user, matching nsh_switch_credentials().
154+
/* seteuid/setegid allow arbitrary IDs only while euid is 0. With
155+
* real UID 0, restore euid 0 before switching, matching
156+
* nsh_switch_credentials().
154157
*/
155158

156159
if (getuid() == 0 && (geteuid() != 0 || getegid() != 0))
@@ -322,6 +325,200 @@ static int multiuser_effective_test(FAR struct mu_ctx_s *ctx)
322325
return ctx->failures;
323326
}
324327

328+
#if CONFIG_SCHED_NGROUPS > 0
329+
static int multiuser_groups_test(FAR struct mu_ctx_s *ctx)
330+
{
331+
gid_t list[NGROUPS_MAX];
332+
gid_t got[NGROUPS_MAX];
333+
int n;
334+
int ret;
335+
336+
printf("multiuser: setgroups/getgroups supplementary IDs\n");
337+
338+
list[0] = MU_GID2;
339+
list[1] = (gid_t)(MU_GID2 + 1);
340+
341+
ret = setgroups(2, list);
342+
if (mu_expect_ok(ctx, "setgroups(2)", ret) != 0)
343+
{
344+
return ctx->failures;
345+
}
346+
347+
n = getgroups(0, NULL);
348+
mu_check_eq(ctx, "getgroups(0) count", n, 2);
349+
350+
n = getgroups(NGROUPS_MAX, got);
351+
mu_check_eq(ctx, "getgroups() count", n, 2);
352+
if (n >= 2)
353+
{
354+
mu_check_eq(ctx, "getgroups()[0]", got[0], list[0]);
355+
mu_check_eq(ctx, "getgroups()[1]", got[1], list[1]);
356+
}
357+
358+
ret = setgroups(0, NULL);
359+
if (mu_expect_ok(ctx, "setgroups(0) clear", ret) != 0)
360+
{
361+
return ctx->failures;
362+
}
363+
364+
n = getgroups(0, NULL);
365+
mu_check_eq(ctx, "getgroups after clear", n, 0);
366+
367+
/* setgroups rejects size > NGROUPS_MAX (no silent truncate). */
368+
369+
ret = setgroups(NGROUPS_MAX + 1, list);
370+
if (ret != 0 && errno == EINVAL)
371+
{
372+
mu_pass("setgroups(NGROUPS_MAX+1) rejected errno=EINVAL");
373+
}
374+
else
375+
{
376+
mu_fail(ctx, "setgroups(NGROUPS_MAX+1): ret=%d errno=%d "
377+
"(expected EINVAL)", ret, errno);
378+
}
379+
380+
#if defined(CONFIG_LIBC_GROUP_FILE)
381+
/* getgrouplist/initgroups fail (do not truncate) when membership >
382+
* NGROUPS_MAX. Temporarily replace the group file for this check.
383+
*/
384+
385+
{
386+
char bak[128];
387+
FILE *fp;
388+
int i;
389+
int need;
390+
gid_t gbuf[NGROUPS_MAX];
391+
int ng;
392+
int saved_errno;
393+
394+
snprintf(bak, sizeof(bak), "%s.bak", CONFIG_LIBC_GROUP_FILEPATH);
395+
unlink(bak);
396+
rename(CONFIG_LIBC_GROUP_FILEPATH, bak);
397+
398+
fp = fopen(CONFIG_LIBC_GROUP_FILEPATH, "w");
399+
if (fp == NULL)
400+
{
401+
mu_fail(ctx, "fopen(%s) for overflow test",
402+
CONFIG_LIBC_GROUP_FILEPATH);
403+
rename(bak, CONFIG_LIBC_GROUP_FILEPATH);
404+
return ctx->failures;
405+
}
406+
407+
/* Primary MU_GID1 plus NGROUPS_MAX distinct supplementary gids. */
408+
409+
for (i = 0; i < NGROUPS_MAX; i++)
410+
{
411+
fprintf(fp, "g%d:*:%d:mu_overflow\n", i, (int)(MU_GID2 + i));
412+
}
413+
414+
fclose(fp);
415+
416+
ng = NGROUPS_MAX;
417+
need = getgrouplist("mu_overflow", MU_GID1, gbuf, &ng);
418+
mu_check_eq(ctx, "getgrouplist overflow returns -1", need, -1);
419+
mu_check_eq(ctx, "getgrouplist reports required size", ng,
420+
NGROUPS_MAX + 1);
421+
422+
saved_errno = 0;
423+
ret = initgroups("mu_overflow", MU_GID1);
424+
if (ret == 0)
425+
{
426+
mu_fail(ctx, "initgroups should fail when groups > NGROUPS_MAX");
427+
}
428+
else
429+
{
430+
saved_errno = errno;
431+
mu_pass("initgroups fails when groups > NGROUPS_MAX (errno=%d)",
432+
saved_errno);
433+
}
434+
435+
unlink(CONFIG_LIBC_GROUP_FILEPATH);
436+
rename(bak, CONFIG_LIBC_GROUP_FILEPATH);
437+
}
438+
#endif /* CONFIG_LIBC_GROUP_FILE */
439+
440+
return ctx->failures;
441+
}
442+
#endif /* CONFIG_SCHED_NGROUPS > 0 */
443+
444+
/****************************************************************************
445+
* Name: multiuser_setres_order_test
446+
*
447+
* Description:
448+
* Regression: setresgid requires euid==0. Dropping uid before gid fails;
449+
* gid-then-uid succeeds (NSH assume_identity order).
450+
*
451+
****************************************************************************/
452+
453+
static int multiuser_setres_order_test(FAR struct mu_ctx_s *ctx)
454+
{
455+
uid_t ruid;
456+
uid_t euid;
457+
uid_t suid;
458+
gid_t rgid;
459+
gid_t egid;
460+
gid_t sgid;
461+
int ret;
462+
463+
printf("multiuser: setresuid/setresgid drop ordering\n");
464+
465+
mu_restore_root(ctx);
466+
467+
/* uid-first must fail once euid is no longer 0. */
468+
469+
ret = setresuid(MU_UID1, MU_UID1, 0);
470+
if (mu_expect_ok(ctx, "setresuid(1000,1000,0) first", ret) != 0)
471+
{
472+
mu_restore_root(ctx);
473+
return ctx->failures;
474+
}
475+
476+
ret = setresgid(MU_GID1, MU_GID1, 0);
477+
mu_expect_denied(ctx, "setresgid after uid drop (wrong order)", ret);
478+
479+
ret = seteuid(0);
480+
if (mu_expect_ok(ctx, "seteuid(0) via suid after bad order", ret) != 0)
481+
{
482+
mu_restore_root(ctx);
483+
return ctx->failures;
484+
}
485+
486+
ret = setresuid(0, 0, 0);
487+
mu_expect_ok(ctx, "setresuid(0,0,0) reset", ret);
488+
ret = setresgid(0, 0, 0);
489+
mu_expect_ok(ctx, "setresgid(0,0,0) reset", ret);
490+
491+
/* gid-then-uid (correct order) must succeed. */
492+
493+
ret = setresgid(MU_GID1, MU_GID1, 0);
494+
if (mu_expect_ok(ctx, "setresgid(1000,1000,0) first", ret) != 0)
495+
{
496+
mu_restore_root(ctx);
497+
return ctx->failures;
498+
}
499+
500+
ret = setresuid(MU_UID1, MU_UID1, 0);
501+
if (mu_expect_ok(ctx, "setresuid(1000,1000,0) second", ret) != 0)
502+
{
503+
mu_restore_root(ctx);
504+
return ctx->failures;
505+
}
506+
507+
getresuid(&ruid, &euid, &suid);
508+
getresgid(&rgid, &egid, &sgid);
509+
mu_check_eq(ctx, "ruid after gid-then-uid", ruid, MU_UID1);
510+
mu_check_eq(ctx, "euid after gid-then-uid", euid, MU_UID1);
511+
mu_check_eq(ctx, "suid after gid-then-uid", suid, 0);
512+
mu_check_eq(ctx, "rgid after gid-then-uid", rgid, MU_GID1);
513+
mu_check_eq(ctx, "egid after gid-then-uid", egid, MU_GID1);
514+
mu_check_eq(ctx, "sgid after gid-then-uid", sgid, 0);
515+
516+
mu_restore_root(ctx);
517+
setresuid(0, 0, 0);
518+
setresgid(0, 0, 0);
519+
return ctx->failures;
520+
}
521+
325522
static int multiuser_resuid_test(FAR struct mu_ctx_s *ctx)
326523
{
327524
uid_t ruid;
@@ -798,7 +995,7 @@ static int multiuser_mqueue_test(FAR struct mu_ctx_s *ctx)
798995

799996
memset(&attr, 0, sizeof(attr));
800997
attr.mq_maxmsg = 4;
801-
attr.mq_msgsize = 64;
998+
attr.mq_msgsize = CONFIG_MQ_MAXMSGSIZE;
802999

8031000
if (mu_set_effective(ctx, MU_UID1, MU_GID1) != 0)
8041001
{
@@ -1146,6 +1343,13 @@ int multiuser_test(void)
11461343
printf("multiuser_test: start\n");
11471344

11481345
multiuser_effective_test(&ctx);
1346+
#if CONFIG_SCHED_NGROUPS > 0
1347+
multiuser_groups_test(&ctx);
1348+
#else
1349+
printf("multiuser: skipping supplementary groups test "
1350+
"(need CONFIG_SCHED_NGROUPS > 0)\n");
1351+
#endif
1352+
multiuser_setres_order_test(&ctx);
11491353
multiuser_resuid_test(&ctx);
11501354

11511355
#if defined(CONFIG_SCHED_WAITPID) && !defined(CONFIG_BUILD_KERNEL)

0 commit comments

Comments
 (0)