mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-22 19:00:07 +00:00
Harden putpwent, putgrent, putspent, putspent against injection [BZ #18724]
This prevents injection of ':' and '\n' into output functions which use the NSS files database syntax. Critical fields (user/group names and file system paths) are checked strictly. For backwards compatibility, the GECOS field is rewritten instead. The getent program is adjusted to use the put*ent functions in libc, instead of local copies. This changes the behavior of getent if user names start with '-' or '+'.
This commit is contained in:
parent
b0f81637d5
commit
676599b36a
38
ChangeLog
38
ChangeLog
@ -1,3 +1,41 @@
|
||||
2015-10-02 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
[BZ #18724]
|
||||
* include/nss.h (NSS_INVALID_FIELD_CHARACTERS): Define.
|
||||
(__nss_invalid_field_characters, __nss_valid_field)
|
||||
(__nss_valid_list_field, __nss_rewrite_field): Declare.
|
||||
* nss/valid_field.c, nss/valid_list_field, nss/rewrite_field.c,
|
||||
tst-field.c: New file.
|
||||
* nss/Makefile (routines): Add valid_field, rewrite_field.
|
||||
(tests-static): Define unconditionally.
|
||||
(tests): Include tests-static.
|
||||
[build-static-nss] (tests-static): Use append.
|
||||
[build-static-nss] (tests): Remove modification.
|
||||
* nss/getent.c (print_group): Call putgrent. Report error.
|
||||
(print_gshadow): Call putsgent. Report error.
|
||||
(print_passwd): Call putpwent. Report error.
|
||||
(print_shadow): Call putspent. Report error.
|
||||
* include/pwd.h: Include <nss.h> instead of <nss/nss.h>.
|
||||
* pwd/pwd.h (putpwent): Remove incorrect nonnull attribute.
|
||||
* pwd/putpwent.c (putpwent): Use ISO function definition. Check
|
||||
name, password, directory, shell fields for valid syntax. Rewrite
|
||||
GECOS field to match syntax.
|
||||
* pwd/Makefile (tests): Add tst-putpwent.
|
||||
* pwd/tst-putpwent.c: New file.
|
||||
* grp/putgrent.c (putgrent): Convert to ISO function definition.
|
||||
Check grName, grpasswd, gr_mem fields for valid syntax.
|
||||
Change loop variable i to size_t.
|
||||
* grp/Makefile (tests): Add tst-putgrent.
|
||||
* grp/tst-putgrent.c: New file.
|
||||
* shadow/putspent.c (putspent): Check sp_namp, sp_pwdp fields for
|
||||
valid syntax.
|
||||
* shadow/Makefile (tests): Add tst-putspent.
|
||||
* shadow/tst-putspent.c: New file.
|
||||
* gshadow/putsgent.c (putsgent): Check sg_namp, sg_passwd, sg_adm,
|
||||
sg_mem fields for valid syntax.
|
||||
* gshadow/Makefile (tests): Add tst-putsgent.
|
||||
* gshadow/tst-putsgent.c: New file.
|
||||
|
||||
2015-10-01 Gabriel F. T. Gomes <gftg@linux.vnet.ibm.com>
|
||||
|
||||
* sysdeps/powerpc/powerpc64/power8/strncpy.S: Added comments to some
|
||||
|
10
NEWS
10
NEWS
@ -13,11 +13,11 @@ Version 2.23
|
||||
15918, 16141, 16296, 16347, 16415, 16517, 16519, 16520, 16521, 16620,
|
||||
16734, 16973, 16985, 17118, 17243, 17244, 17250, 17441, 17787, 17886,
|
||||
17887, 17905, 18084, 18086, 18240, 18265, 18370, 18421, 18480, 18525,
|
||||
18595, 18610, 18618, 18647, 18661, 18674, 18675, 18681, 18757, 18778,
|
||||
18781, 18787, 18789, 18790, 18795, 18796, 18803, 18820, 18823, 18824,
|
||||
18825, 18857, 18863, 18870, 18872, 18873, 18875, 18887, 18921, 18951,
|
||||
18952, 18956, 18961, 18966, 18967, 18969, 18970, 18977, 18980, 18981,
|
||||
18985, 19003, 19016, 19032, 19046.
|
||||
18595, 18610, 18618, 18647, 18661, 18674, 18675, 18681, 18724, 18757,
|
||||
18778, 18781, 18787, 18789, 18790, 18795, 18796, 18803, 18820, 18823,
|
||||
18824, 18825, 18857, 18863, 18870, 18872, 18873, 18875, 18887, 18921,
|
||||
18951, 18952, 18956, 18961, 18966, 18967, 18969, 18970, 18977, 18980,
|
||||
18981, 18985, 19003, 19016, 19032, 19046.
|
||||
|
||||
* The obsolete header <regexp.h> has been removed. Programs that require
|
||||
this header must be updated to use <regex.h> instead.
|
||||
|
@ -28,7 +28,7 @@ routines := fgetgrent initgroups setgroups \
|
||||
getgrent getgrgid getgrnam putgrent \
|
||||
getgrent_r getgrgid_r getgrnam_r fgetgrent_r
|
||||
|
||||
tests := testgrp
|
||||
tests := testgrp tst-putgrent
|
||||
|
||||
ifeq (yes,$(build-shared))
|
||||
test-srcs := tst_fgetgrent
|
||||
|
@ -16,7 +16,9 @@
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <nss.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <grp.h>
|
||||
|
||||
#define flockfile(s) _IO_flockfile (s)
|
||||
@ -27,13 +29,14 @@
|
||||
/* Write an entry to the given stream.
|
||||
This must know the format of the group file. */
|
||||
int
|
||||
putgrent (gr, stream)
|
||||
const struct group *gr;
|
||||
FILE *stream;
|
||||
putgrent (const struct group *gr, FILE *stream)
|
||||
{
|
||||
int retval;
|
||||
|
||||
if (__glibc_unlikely (gr == NULL) || __glibc_unlikely (stream == NULL))
|
||||
if (__glibc_unlikely (gr == NULL) || __glibc_unlikely (stream == NULL)
|
||||
|| gr->gr_name == NULL || !__nss_valid_field (gr->gr_name)
|
||||
|| !__nss_valid_field (gr->gr_passwd)
|
||||
|| !__nss_valid_list_field (gr->gr_mem))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
@ -56,9 +59,7 @@ putgrent (gr, stream)
|
||||
|
||||
if (gr->gr_mem != NULL)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; gr->gr_mem[i] != NULL; i++)
|
||||
for (size_t i = 0; gr->gr_mem[i] != NULL; i++)
|
||||
if (fprintf (stream, i == 0 ? "%s" : ",%s", gr->gr_mem[i]) < 0)
|
||||
{
|
||||
/* What else can we do? */
|
||||
|
167
grp/tst-putgrent.c
Normal file
167
grp/tst-putgrent.c
Normal file
@ -0,0 +1,167 @@
|
||||
/* Test for processing of invalid group entries. [BZ #18724]
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool errors;
|
||||
|
||||
static void
|
||||
check (struct group e, const char *expected)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
FILE *f = open_memstream (&buf, &buf_size);
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
printf ("open_memstream: %m\n");
|
||||
errors = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = putgrent (&e, f);
|
||||
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
printf ("putgrent: unexpected error code: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("putgrent: unexpected success (\"%s\", \"%s\")\n",
|
||||
e.gr_name, e.gr_passwd);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expect success. */
|
||||
size_t expected_length = strlen (expected);
|
||||
if (ret == 0)
|
||||
{
|
||||
long written = ftell (f);
|
||||
|
||||
if (written <= 0 || fflush (f) < 0)
|
||||
{
|
||||
printf ("stream error: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
else if (buf[written - 1] != '\n')
|
||||
{
|
||||
printf ("FAILED: \"%s\" without newline\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
else if (strncmp (buf, expected, written - 1) != 0
|
||||
|| written - 1 != expected_length)
|
||||
{
|
||||
buf[written - 1] = '\0';
|
||||
printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
|
||||
buf, written - 1, expected, expected_length);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("FAILED: putgrent (expected \"%s\"): %m\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) "root",
|
||||
},
|
||||
"root::0:");
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) "root",
|
||||
.gr_passwd = (char *) "password",
|
||||
.gr_gid = 1234,
|
||||
.gr_mem = (char *[2]) {(char *) "member1", NULL}
|
||||
},
|
||||
"root:password:1234:member1");
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) "root",
|
||||
.gr_passwd = (char *) "password",
|
||||
.gr_gid = 1234,
|
||||
.gr_mem = (char *[3]) {(char *) "member1", (char *) "member2", NULL}
|
||||
},
|
||||
"root:password:1234:member1,member2");
|
||||
|
||||
/* Bad values. */
|
||||
{
|
||||
static const char *const bad_strings[] = {
|
||||
":",
|
||||
"\n",
|
||||
":bad",
|
||||
"\nbad",
|
||||
"b:ad",
|
||||
"b\nad",
|
||||
"bad:",
|
||||
"bad\n",
|
||||
"b:a\nd"
|
||||
",",
|
||||
"\n,",
|
||||
":,",
|
||||
",bad",
|
||||
"b,ad",
|
||||
"bad,",
|
||||
NULL
|
||||
};
|
||||
for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
|
||||
{
|
||||
char *members[]
|
||||
= {(char *) "first", (char *) *bad, (char *) "last", NULL};
|
||||
if (strpbrk (*bad, ":\n") != NULL)
|
||||
{
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) "root",
|
||||
.gr_passwd = (char *) *bad,
|
||||
}, NULL);
|
||||
}
|
||||
check ((struct group) {
|
||||
.gr_name = (char *) "root",
|
||||
.gr_passwd = (char *) "password",
|
||||
.gr_mem = members,
|
||||
}, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
@ -26,7 +26,7 @@ headers = gshadow.h
|
||||
routines = getsgent getsgnam sgetsgent fgetsgent putsgent \
|
||||
getsgent_r getsgnam_r sgetsgent_r fgetsgent_r
|
||||
|
||||
tests = tst-gshadow
|
||||
tests = tst-gshadow tst-putsgent
|
||||
|
||||
CFLAGS-getsgent_r.c = -fexceptions
|
||||
CFLAGS-getsgent.c = -fexceptions
|
||||
|
@ -15,9 +15,11 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <gshadow.h>
|
||||
#include <nss.h>
|
||||
|
||||
#define _S(x) x ? x : ""
|
||||
|
||||
@ -29,6 +31,15 @@ putsgent (const struct sgrp *g, FILE *stream)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
if (g->sg_namp == NULL || !__nss_valid_field (g->sg_namp)
|
||||
|| !__nss_valid_field (g->sg_passwd)
|
||||
|| !__nss_valid_list_field (g->sg_adm)
|
||||
|| !__nss_valid_list_field (g->sg_mem))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_IO_flockfile (stream);
|
||||
|
||||
if (fprintf (stream, "%s:%s:", g->sg_namp, _S (g->sg_passwd)) < 0)
|
||||
|
168
gshadow/tst-putsgent.c
Normal file
168
gshadow/tst-putsgent.c
Normal file
@ -0,0 +1,168 @@
|
||||
/* Test for processing of invalid gshadow entries. [BZ #18724]
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <gshadow.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool errors;
|
||||
|
||||
static void
|
||||
check (struct sgrp e, const char *expected)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
FILE *f = open_memstream (&buf, &buf_size);
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
printf ("open_memstream: %m\n");
|
||||
errors = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = putsgent (&e, f);
|
||||
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
printf ("putsgent: unexpected error code: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("putsgent: unexpected success (\"%s\")\n", e.sg_namp);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expect success. */
|
||||
size_t expected_length = strlen (expected);
|
||||
if (ret == 0)
|
||||
{
|
||||
long written = ftell (f);
|
||||
|
||||
if (written <= 0 || fflush (f) < 0)
|
||||
{
|
||||
printf ("stream error: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
else if (buf[written - 1] != '\n')
|
||||
{
|
||||
printf ("FAILED: \"%s\" without newline\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
else if (strncmp (buf, expected, written - 1) != 0
|
||||
|| written - 1 != expected_length)
|
||||
{
|
||||
printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
|
||||
buf, written - 1, expected, expected_length);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("FAILED: putsgent (expected \"%s\"): %m\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
},
|
||||
"root:::");
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
.sg_passwd = (char *) "password",
|
||||
},
|
||||
"root:password::");
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
.sg_passwd = (char *) "password",
|
||||
.sg_adm = (char *[]) {(char *) "adm1", (char *) "adm2", NULL},
|
||||
.sg_mem = (char *[]) {(char *) "mem1", (char *) "mem2", NULL},
|
||||
},
|
||||
"root:password:adm1,adm2:mem1,mem2");
|
||||
|
||||
/* Bad values. */
|
||||
{
|
||||
static const char *const bad_strings[] = {
|
||||
":",
|
||||
"\n",
|
||||
":bad",
|
||||
"\nbad",
|
||||
"b:ad",
|
||||
"b\nad",
|
||||
"bad:",
|
||||
"bad\n",
|
||||
"b:a\nd",
|
||||
",",
|
||||
"\n,",
|
||||
":,",
|
||||
",bad",
|
||||
"b,ad",
|
||||
"bad,",
|
||||
NULL
|
||||
};
|
||||
for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
|
||||
{
|
||||
char *members[]
|
||||
= {(char *) "first", (char *) *bad, (char *) "last", NULL};
|
||||
if (strpbrk (*bad, ":\n") != NULL)
|
||||
{
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
.sg_passwd = (char *) *bad,
|
||||
}, NULL);
|
||||
}
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
.sg_passwd = (char *) "password",
|
||||
.sg_adm = members
|
||||
}, NULL);
|
||||
check ((struct sgrp) {
|
||||
.sg_namp = (char *) "root",
|
||||
.sg_passwd = (char *) "password",
|
||||
.sg_mem = members
|
||||
}, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
@ -1 +1,14 @@
|
||||
#ifndef _NSS_H
|
||||
#include <nss/nss.h>
|
||||
|
||||
#define NSS_INVALID_FIELD_CHARACTERS ":\n"
|
||||
extern const char __nss_invalid_field_characters[] attribute_hidden;
|
||||
|
||||
_Bool __nss_valid_field (const char *value)
|
||||
attribute_hidden internal_function;
|
||||
_Bool __nss_valid_list_field (char **list)
|
||||
attribute_hidden internal_function;
|
||||
const char *__nss_rewrite_field (const char *value, char **to_be_freed)
|
||||
attribute_hidden internal_function;
|
||||
|
||||
#endif /* _NSS_H */
|
||||
|
@ -24,7 +24,7 @@ extern int __fgetpwent_r (FILE * __stream, struct passwd *__resultbuf,
|
||||
char *__buffer, size_t __buflen,
|
||||
struct passwd **__result);
|
||||
|
||||
#include <nss/nss.h>
|
||||
#include <nss.h>
|
||||
|
||||
struct parser_data;
|
||||
extern int _nss_files_parse_pwent (char *line, struct passwd *result,
|
||||
|
@ -26,6 +26,7 @@ headers := nss.h
|
||||
|
||||
# This is the trivial part which goes into libc itself.
|
||||
routines = nsswitch getnssent getnssent_r digits_dots \
|
||||
valid_field valid_list_field rewrite_field \
|
||||
$(addsuffix -lookup,$(databases))
|
||||
|
||||
# These are the databases that go through nss dispatch.
|
||||
@ -47,8 +48,10 @@ install-bin := getent makedb
|
||||
makedb-modules = xmalloc hash-string
|
||||
extra-objs += $(makedb-modules:=.o)
|
||||
|
||||
tests-static = tst-field
|
||||
tests = test-netdb tst-nss-test1 test-digits-dots \
|
||||
tst-nss-getpwent bug17079
|
||||
tst-nss-getpwent bug17079 \
|
||||
$(tests-static)
|
||||
xtests = bug-erange
|
||||
|
||||
# Specify rules for the nss_* modules. We have some services.
|
||||
@ -83,8 +86,7 @@ libnss_db-inhibit-o = $(filter-out .os,$(object-suffixes))
|
||||
ifeq ($(build-static-nss),yes)
|
||||
routines += $(libnss_files-routines)
|
||||
static-only-routines += $(libnss_files-routines)
|
||||
tests-static = tst-nss-static
|
||||
tests += $(tests-static)
|
||||
tests-static += tst-nss-static
|
||||
endif
|
||||
|
||||
include ../Rules
|
||||
|
76
nss/getent.c
76
nss/getent.c
@ -184,20 +184,8 @@ ethers_keys (int number, char *key[])
|
||||
static void
|
||||
print_group (struct group *grp)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
printf ("%s:%s:%lu:", grp->gr_name ? grp->gr_name : "",
|
||||
grp->gr_passwd ? grp->gr_passwd : "",
|
||||
(unsigned long int) grp->gr_gid);
|
||||
|
||||
while (grp->gr_mem[i] != NULL)
|
||||
{
|
||||
fputs_unlocked (grp->gr_mem[i], stdout);
|
||||
++i;
|
||||
if (grp->gr_mem[i] != NULL)
|
||||
putchar_unlocked (',');
|
||||
}
|
||||
putchar_unlocked ('\n');
|
||||
if (putgrent (grp, stdout) != 0)
|
||||
fprintf (stderr, "error writing group entry: %m\n");
|
||||
}
|
||||
|
||||
static int
|
||||
@ -241,32 +229,8 @@ group_keys (int number, char *key[])
|
||||
static void
|
||||
print_gshadow (struct sgrp *sg)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
printf ("%s:%s:",
|
||||
sg->sg_namp ? sg->sg_namp : "",
|
||||
sg->sg_passwd ? sg->sg_passwd : "");
|
||||
|
||||
while (sg->sg_adm[i] != NULL)
|
||||
{
|
||||
fputs_unlocked (sg->sg_adm[i], stdout);
|
||||
++i;
|
||||
if (sg->sg_adm[i] != NULL)
|
||||
putchar_unlocked (',');
|
||||
}
|
||||
|
||||
putchar_unlocked (':');
|
||||
|
||||
i = 0;
|
||||
while (sg->sg_mem[i] != NULL)
|
||||
{
|
||||
fputs_unlocked (sg->sg_mem[i], stdout);
|
||||
++i;
|
||||
if (sg->sg_mem[i] != NULL)
|
||||
putchar_unlocked (',');
|
||||
}
|
||||
|
||||
putchar_unlocked ('\n');
|
||||
if (putsgent (sg, stdout) != 0)
|
||||
fprintf (stderr, "error writing gshadow entry: %m\n");
|
||||
}
|
||||
|
||||
static int
|
||||
@ -603,14 +567,8 @@ networks_keys (int number, char *key[])
|
||||
static void
|
||||
print_passwd (struct passwd *pwd)
|
||||
{
|
||||
printf ("%s:%s:%lu:%lu:%s:%s:%s\n",
|
||||
pwd->pw_name ? pwd->pw_name : "",
|
||||
pwd->pw_passwd ? pwd->pw_passwd : "",
|
||||
(unsigned long int) pwd->pw_uid,
|
||||
(unsigned long int) pwd->pw_gid,
|
||||
pwd->pw_gecos ? pwd->pw_gecos : "",
|
||||
pwd->pw_dir ? pwd->pw_dir : "",
|
||||
pwd->pw_shell ? pwd->pw_shell : "");
|
||||
if (putpwent (pwd, stdout) != 0)
|
||||
fprintf (stderr, "error writing passwd entry: %m\n");
|
||||
}
|
||||
|
||||
static int
|
||||
@ -812,26 +770,8 @@ services_keys (int number, char *key[])
|
||||
static void
|
||||
print_shadow (struct spwd *sp)
|
||||
{
|
||||
printf ("%s:%s:",
|
||||
sp->sp_namp ? sp->sp_namp : "",
|
||||
sp->sp_pwdp ? sp->sp_pwdp : "");
|
||||
|
||||
#define SHADOW_FIELD(n) \
|
||||
if (sp->n == -1) \
|
||||
putchar_unlocked (':'); \
|
||||
else \
|
||||
printf ("%ld:", sp->n)
|
||||
|
||||
SHADOW_FIELD (sp_lstchg);
|
||||
SHADOW_FIELD (sp_min);
|
||||
SHADOW_FIELD (sp_max);
|
||||
SHADOW_FIELD (sp_warn);
|
||||
SHADOW_FIELD (sp_inact);
|
||||
SHADOW_FIELD (sp_expire);
|
||||
if (sp->sp_flag == ~0ul)
|
||||
putchar_unlocked ('\n');
|
||||
else
|
||||
printf ("%lu\n", sp->sp_flag);
|
||||
if (putspent (sp, stdout) != 0)
|
||||
fprintf (stderr, "error writing shadow entry: %m\n");
|
||||
}
|
||||
|
||||
static int
|
||||
|
51
nss/rewrite_field.c
Normal file
51
nss/rewrite_field.c
Normal file
@ -0,0 +1,51 @@
|
||||
/* Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <nss.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Rewrite VALUE to a valid field value in the NSS database. Invalid
|
||||
characters are replaced with a single space character ' '. If
|
||||
VALUE is NULL, the empty string is returned. *TO_BE_FREED is
|
||||
overwritten with a pointer the caller has to free if the function
|
||||
returns successfully. On failure, return NULL. */
|
||||
const char *
|
||||
__nss_rewrite_field (const char *value, char **to_be_freed)
|
||||
{
|
||||
*to_be_freed = NULL;
|
||||
if (value == NULL)
|
||||
return "";
|
||||
|
||||
/* Search for non-allowed characters. */
|
||||
const char *p = strpbrk (value, __nss_invalid_field_characters);
|
||||
if (p == NULL)
|
||||
return value;
|
||||
*to_be_freed = __strdup (value);
|
||||
if (*to_be_freed == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Switch pointer to freshly-allocated buffer. */
|
||||
char *bad = *to_be_freed + (p - value);
|
||||
do
|
||||
{
|
||||
*bad = ' ';
|
||||
bad = strpbrk (bad + 1, __nss_invalid_field_characters);
|
||||
}
|
||||
while (bad != NULL);
|
||||
|
||||
return *to_be_freed;
|
||||
}
|
101
nss/tst-field.c
Normal file
101
nss/tst-field.c
Normal file
@ -0,0 +1,101 @@
|
||||
/* Test for invalid field handling in file-style NSS databases. [BZ #18724]
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* This test needs to be statically linked because it access hidden
|
||||
functions. */
|
||||
|
||||
#include <nss.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool errors;
|
||||
|
||||
static void
|
||||
check (const char *what, bool expr)
|
||||
{
|
||||
if (!expr)
|
||||
{
|
||||
printf ("FAIL: %s\n", what);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(expr) check (#expr, (expr))
|
||||
|
||||
static void
|
||||
check_rewrite (const char *input, const char *expected)
|
||||
{
|
||||
char *to_free;
|
||||
const char *result = __nss_rewrite_field (input, &to_free);
|
||||
CHECK (result != NULL);
|
||||
if (result != NULL && strcmp (result, expected) != 0)
|
||||
{
|
||||
printf ("FAIL: rewrite \"%s\" -> \"%s\", expected \"%s\"\n",
|
||||
input, result, expected);
|
||||
errors = true;
|
||||
}
|
||||
free (to_free);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
CHECK (__nss_valid_field (NULL));
|
||||
CHECK (__nss_valid_field (""));
|
||||
CHECK (__nss_valid_field ("+"));
|
||||
CHECK (__nss_valid_field ("-"));
|
||||
CHECK (__nss_valid_field (" "));
|
||||
CHECK (__nss_valid_field ("abcdef"));
|
||||
CHECK (__nss_valid_field ("abc def"));
|
||||
CHECK (__nss_valid_field ("abc\tdef"));
|
||||
CHECK (!__nss_valid_field ("abcdef:"));
|
||||
CHECK (!__nss_valid_field ("abcde:f"));
|
||||
CHECK (!__nss_valid_field (":abcdef"));
|
||||
CHECK (!__nss_valid_field ("abcdef\n"));
|
||||
CHECK (!__nss_valid_field ("\nabcdef"));
|
||||
CHECK (!__nss_valid_field (":"));
|
||||
CHECK (!__nss_valid_field ("\n"));
|
||||
|
||||
CHECK (__nss_valid_list_field (NULL));
|
||||
CHECK (__nss_valid_list_field ((char *[]) {(char *) "good", NULL}));
|
||||
CHECK (!__nss_valid_list_field ((char *[]) {(char *) "g,ood", NULL}));
|
||||
CHECK (!__nss_valid_list_field ((char *[]) {(char *) "g\nood", NULL}));
|
||||
CHECK (!__nss_valid_list_field ((char *[]) {(char *) "g:ood", NULL}));
|
||||
|
||||
check_rewrite (NULL, "");
|
||||
check_rewrite ("", "");
|
||||
check_rewrite ("abc", "abc");
|
||||
check_rewrite ("abc\n", "abc ");
|
||||
check_rewrite ("abc:", "abc ");
|
||||
check_rewrite ("\nabc", " abc");
|
||||
check_rewrite (":abc", " abc");
|
||||
check_rewrite (":", " ");
|
||||
check_rewrite ("\n", " ");
|
||||
check_rewrite ("a:b:c", "a b c");
|
||||
check_rewrite ("a\nb\nc", "a b c");
|
||||
check_rewrite ("a\nb:c", "a b c");
|
||||
check_rewrite ("aa\nbb\ncc", "aa bb cc");
|
||||
check_rewrite ("aa\nbb:cc", "aa bb cc");
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
31
nss/valid_field.c
Normal file
31
nss/valid_field.c
Normal file
@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <nss.h>
|
||||
#include <string.h>
|
||||
|
||||
const char __nss_invalid_field_characters[] = NSS_INVALID_FIELD_CHARACTERS;
|
||||
|
||||
/* Check that VALUE is either NULL or a NUL-terminated string which
|
||||
does not contain characters not permitted in NSS database
|
||||
fields. */
|
||||
_Bool
|
||||
__nss_valid_field (const char *value)
|
||||
{
|
||||
return value == NULL
|
||||
|| strpbrk (value, __nss_invalid_field_characters) == NULL;
|
||||
}
|
35
nss/valid_list_field.c
Normal file
35
nss/valid_list_field.c
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <nss.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char invalid_characters[] = NSS_INVALID_FIELD_CHARACTERS ",";
|
||||
|
||||
/* Check that all list members match the field syntax requirements and
|
||||
do not contain the character ','. */
|
||||
_Bool
|
||||
__nss_valid_list_field (char **list)
|
||||
{
|
||||
if (list == NULL)
|
||||
return true;
|
||||
for (; *list != NULL; ++list)
|
||||
if (strpbrk (*list, invalid_characters) != NULL)
|
||||
return false;
|
||||
return true;
|
||||
}
|
@ -28,7 +28,7 @@ routines := fgetpwent getpw putpwent \
|
||||
getpwent getpwnam getpwuid \
|
||||
getpwent_r getpwnam_r getpwuid_r fgetpwent_r
|
||||
|
||||
tests := tst-getpw
|
||||
tests := tst-getpw tst-putpwent
|
||||
|
||||
include ../Rules
|
||||
|
||||
|
@ -18,37 +18,47 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
#include <nss.h>
|
||||
|
||||
#define _S(x) x ?: ""
|
||||
|
||||
/* Write an entry to the given stream.
|
||||
This must know the format of the password file. */
|
||||
/* Write an entry to the given stream. This must know the format of
|
||||
the password file. If the input contains invalid characters,
|
||||
return EINVAL, or replace them with spaces (if they are contained
|
||||
in the GECOS field). */
|
||||
int
|
||||
putpwent (p, stream)
|
||||
const struct passwd *p;
|
||||
FILE *stream;
|
||||
putpwent (const struct passwd *p, FILE *stream)
|
||||
{
|
||||
if (p == NULL || stream == NULL)
|
||||
if (p == NULL || stream == NULL
|
||||
|| p->pw_name == NULL || !__nss_valid_field (p->pw_name)
|
||||
|| !__nss_valid_field (p->pw_passwd)
|
||||
|| !__nss_valid_field (p->pw_dir)
|
||||
|| !__nss_valid_field (p->pw_shell))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret;
|
||||
char *gecos_alloc;
|
||||
const char *gecos = __nss_rewrite_field (p->pw_gecos, &gecos_alloc);
|
||||
|
||||
if (gecos == NULL)
|
||||
return -1;
|
||||
|
||||
if (p->pw_name[0] == '+' || p->pw_name[0] == '-')
|
||||
{
|
||||
if (fprintf (stream, "%s:%s:::%s:%s:%s\n",
|
||||
p->pw_name, _S (p->pw_passwd),
|
||||
_S (p->pw_gecos), _S (p->pw_dir), _S (p->pw_shell)) < 0)
|
||||
return -1;
|
||||
}
|
||||
ret = fprintf (stream, "%s:%s:::%s:%s:%s\n",
|
||||
p->pw_name, _S (p->pw_passwd),
|
||||
gecos, _S (p->pw_dir), _S (p->pw_shell));
|
||||
else
|
||||
{
|
||||
if (fprintf (stream, "%s:%s:%lu:%lu:%s:%s:%s\n",
|
||||
p->pw_name, _S (p->pw_passwd),
|
||||
(unsigned long int) p->pw_uid,
|
||||
(unsigned long int) p->pw_gid,
|
||||
_S (p->pw_gecos), _S (p->pw_dir), _S (p->pw_shell)) < 0)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
ret = fprintf (stream, "%s:%s:%lu:%lu:%s:%s:%s\n",
|
||||
p->pw_name, _S (p->pw_passwd),
|
||||
(unsigned long int) p->pw_uid,
|
||||
(unsigned long int) p->pw_gid,
|
||||
gecos, _S (p->pw_dir), _S (p->pw_shell));
|
||||
|
||||
free (gecos_alloc);
|
||||
if (ret >= 0)
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
168
pwd/tst-putpwent.c
Normal file
168
pwd/tst-putpwent.c
Normal file
@ -0,0 +1,168 @@
|
||||
/* Test for processing of invalid passwd entries. [BZ #18724]
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool errors;
|
||||
|
||||
static void
|
||||
check (struct passwd p, const char *expected)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
FILE *f = open_memstream (&buf, &buf_size);
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
printf ("open_memstream: %m\n");
|
||||
errors = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = putpwent (&p, f);
|
||||
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
printf ("putpwent: unexpected error code: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("putpwent: unexpected success (\"%s\")\n", p.pw_name);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expect success. */
|
||||
size_t expected_length = strlen (expected);
|
||||
if (ret == 0)
|
||||
{
|
||||
long written = ftell (f);
|
||||
|
||||
if (written <= 0 || fflush (f) < 0)
|
||||
{
|
||||
printf ("stream error: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
else if (buf[written - 1] != '\n')
|
||||
{
|
||||
printf ("FAILED: \"%s\" without newline\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
else if (strncmp (buf, expected, written - 1) != 0
|
||||
|| written - 1 != expected_length)
|
||||
{
|
||||
printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
|
||||
buf, written - 1, expected, expected_length);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("FAILED: putpwent (expected \"%s\"): %m\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
},
|
||||
"root::0:0:::");
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_passwd = (char *) "password",
|
||||
},
|
||||
"root:password:0:0:::");
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_passwd = (char *) "password",
|
||||
.pw_uid = 12,
|
||||
.pw_gid = 34,
|
||||
.pw_gecos = (char *) "gecos",
|
||||
.pw_dir = (char *) "home",
|
||||
.pw_shell = (char *) "shell",
|
||||
},
|
||||
"root:password:12:34:gecos:home:shell");
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_passwd = (char *) "password",
|
||||
.pw_uid = 12,
|
||||
.pw_gid = 34,
|
||||
.pw_gecos = (char *) ":ge\n:cos\n",
|
||||
.pw_dir = (char *) "home",
|
||||
.pw_shell = (char *) "shell",
|
||||
},
|
||||
"root:password:12:34: ge cos :home:shell");
|
||||
|
||||
/* Bad values. */
|
||||
{
|
||||
static const char *const bad_strings[] = {
|
||||
":",
|
||||
"\n",
|
||||
":bad",
|
||||
"\nbad",
|
||||
"b:ad",
|
||||
"b\nad",
|
||||
"bad:",
|
||||
"bad\n",
|
||||
"b:a\nd",
|
||||
NULL
|
||||
};
|
||||
for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
|
||||
{
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_passwd = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_dir = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct passwd) {
|
||||
.pw_name = (char *) "root",
|
||||
.pw_shell = (char *) *bad,
|
||||
}, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return errors > 0;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
@ -27,7 +27,7 @@ routines = getspent getspnam sgetspent fgetspent putspent \
|
||||
getspent_r getspnam_r sgetspent_r fgetspent_r \
|
||||
lckpwdf
|
||||
|
||||
tests = tst-shadow
|
||||
tests = tst-shadow tst-putspent
|
||||
|
||||
CFLAGS-getspent_r.c = -fexceptions
|
||||
CFLAGS-getspent.c = -fexceptions
|
||||
|
@ -15,6 +15,8 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <nss.h>
|
||||
#include <stdio.h>
|
||||
#include <shadow.h>
|
||||
|
||||
@ -31,6 +33,13 @@ putspent (const struct spwd *p, FILE *stream)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
if (p->sp_namp == NULL || !__nss_valid_field (p->sp_namp)
|
||||
|| !__nss_valid_field (p->sp_pwdp))
|
||||
{
|
||||
__set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
flockfile (stream);
|
||||
|
||||
if (fprintf (stream, "%s:%s:", p->sp_namp, _S (p->sp_pwdp)) < 0)
|
||||
|
164
shadow/tst-putspent.c
Normal file
164
shadow/tst-putspent.c
Normal file
@ -0,0 +1,164 @@
|
||||
/* Test for processing of invalid shadow entries. [BZ #18724]
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <shadow.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool errors;
|
||||
|
||||
static void
|
||||
check (struct spwd p, const char *expected)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
FILE *f = open_memstream (&buf, &buf_size);
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
printf ("open_memstream: %m\n");
|
||||
errors = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = putspent (&p, f);
|
||||
|
||||
if (expected == NULL)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != EINVAL)
|
||||
{
|
||||
printf ("putspent: unexpected error code: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("putspent: unexpected success (\"%s\")\n", p.sp_namp);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expect success. */
|
||||
size_t expected_length = strlen (expected);
|
||||
if (ret == 0)
|
||||
{
|
||||
long written = ftell (f);
|
||||
|
||||
if (written <= 0 || fflush (f) < 0)
|
||||
{
|
||||
printf ("stream error: %m\n");
|
||||
errors = true;
|
||||
}
|
||||
else if (buf[written - 1] != '\n')
|
||||
{
|
||||
printf ("FAILED: \"%s\" without newline\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
else if (strncmp (buf, expected, written - 1) != 0
|
||||
|| written - 1 != expected_length)
|
||||
{
|
||||
printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
|
||||
buf, written - 1, expected, expected_length);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("FAILED: putspent (expected \"%s\"): %m\n", expected);
|
||||
errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
free (buf);
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) "root",
|
||||
},
|
||||
"root::0:0:0:0:0:0:0");
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) "root",
|
||||
.sp_pwdp = (char *) "password",
|
||||
},
|
||||
"root:password:0:0:0:0:0:0:0");
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) "root",
|
||||
.sp_pwdp = (char *) "password",
|
||||
.sp_lstchg = -1,
|
||||
.sp_min = -1,
|
||||
.sp_max = -1,
|
||||
.sp_warn = -1,
|
||||
.sp_inact = -1,
|
||||
.sp_expire = -1,
|
||||
.sp_flag = -1
|
||||
},
|
||||
"root:password:::::::");
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) "root",
|
||||
.sp_pwdp = (char *) "password",
|
||||
.sp_lstchg = 1,
|
||||
.sp_min = 2,
|
||||
.sp_max = 3,
|
||||
.sp_warn = 4,
|
||||
.sp_inact = 5,
|
||||
.sp_expire = 6,
|
||||
.sp_flag = 7
|
||||
},
|
||||
"root:password:1:2:3:4:5:6:7");
|
||||
|
||||
/* Bad values. */
|
||||
{
|
||||
static const char *const bad_strings[] = {
|
||||
":",
|
||||
"\n",
|
||||
":bad",
|
||||
"\nbad",
|
||||
"b:ad",
|
||||
"b\nad",
|
||||
"bad:",
|
||||
"bad\n",
|
||||
"b:a\nd",
|
||||
NULL
|
||||
};
|
||||
for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
|
||||
{
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) *bad,
|
||||
}, NULL);
|
||||
check ((struct spwd) {
|
||||
.sp_namp = (char *) "root",
|
||||
.sp_pwdp = (char *) *bad,
|
||||
}, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
#define TEST_FUNCTION do_test ()
|
||||
#include "../test-skeleton.c"
|
Loading…
Reference in New Issue
Block a user