mirror of
https://sourceware.org/git/glibc.git
synced 2025-01-03 16:21:06 +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>
|
2015-10-01 Gabriel F. T. Gomes <gftg@linux.vnet.ibm.com>
|
||||||
|
|
||||||
* sysdeps/powerpc/powerpc64/power8/strncpy.S: Added comments to some
|
* 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,
|
15918, 16141, 16296, 16347, 16415, 16517, 16519, 16520, 16521, 16620,
|
||||||
16734, 16973, 16985, 17118, 17243, 17244, 17250, 17441, 17787, 17886,
|
16734, 16973, 16985, 17118, 17243, 17244, 17250, 17441, 17787, 17886,
|
||||||
17887, 17905, 18084, 18086, 18240, 18265, 18370, 18421, 18480, 18525,
|
17887, 17905, 18084, 18086, 18240, 18265, 18370, 18421, 18480, 18525,
|
||||||
18595, 18610, 18618, 18647, 18661, 18674, 18675, 18681, 18757, 18778,
|
18595, 18610, 18618, 18647, 18661, 18674, 18675, 18681, 18724, 18757,
|
||||||
18781, 18787, 18789, 18790, 18795, 18796, 18803, 18820, 18823, 18824,
|
18778, 18781, 18787, 18789, 18790, 18795, 18796, 18803, 18820, 18823,
|
||||||
18825, 18857, 18863, 18870, 18872, 18873, 18875, 18887, 18921, 18951,
|
18824, 18825, 18857, 18863, 18870, 18872, 18873, 18875, 18887, 18921,
|
||||||
18952, 18956, 18961, 18966, 18967, 18969, 18970, 18977, 18980, 18981,
|
18951, 18952, 18956, 18961, 18966, 18967, 18969, 18970, 18977, 18980,
|
||||||
18985, 19003, 19016, 19032, 19046.
|
18981, 18985, 19003, 19016, 19032, 19046.
|
||||||
|
|
||||||
* The obsolete header <regexp.h> has been removed. Programs that require
|
* The obsolete header <regexp.h> has been removed. Programs that require
|
||||||
this header must be updated to use <regex.h> instead.
|
this header must be updated to use <regex.h> instead.
|
||||||
|
@ -28,7 +28,7 @@ routines := fgetgrent initgroups setgroups \
|
|||||||
getgrent getgrgid getgrnam putgrent \
|
getgrent getgrgid getgrnam putgrent \
|
||||||
getgrent_r getgrgid_r getgrnam_r fgetgrent_r
|
getgrent_r getgrgid_r getgrnam_r fgetgrent_r
|
||||||
|
|
||||||
tests := testgrp
|
tests := testgrp tst-putgrent
|
||||||
|
|
||||||
ifeq (yes,$(build-shared))
|
ifeq (yes,$(build-shared))
|
||||||
test-srcs := tst_fgetgrent
|
test-srcs := tst_fgetgrent
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <nss.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
|
|
||||||
#define flockfile(s) _IO_flockfile (s)
|
#define flockfile(s) _IO_flockfile (s)
|
||||||
@ -27,13 +29,14 @@
|
|||||||
/* Write an entry to the given stream.
|
/* Write an entry to the given stream.
|
||||||
This must know the format of the group file. */
|
This must know the format of the group file. */
|
||||||
int
|
int
|
||||||
putgrent (gr, stream)
|
putgrent (const struct group *gr, FILE *stream)
|
||||||
const struct group *gr;
|
|
||||||
FILE *stream;
|
|
||||||
{
|
{
|
||||||
int retval;
|
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);
|
__set_errno (EINVAL);
|
||||||
return -1;
|
return -1;
|
||||||
@ -56,9 +59,7 @@ putgrent (gr, stream)
|
|||||||
|
|
||||||
if (gr->gr_mem != NULL)
|
if (gr->gr_mem != NULL)
|
||||||
{
|
{
|
||||||
int i;
|
for (size_t i = 0; gr->gr_mem[i] != NULL; i++)
|
||||||
|
|
||||||
for (i = 0 ; gr->gr_mem[i] != NULL; i++)
|
|
||||||
if (fprintf (stream, i == 0 ? "%s" : ",%s", gr->gr_mem[i]) < 0)
|
if (fprintf (stream, i == 0 ? "%s" : ",%s", gr->gr_mem[i]) < 0)
|
||||||
{
|
{
|
||||||
/* What else can we do? */
|
/* 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 \
|
routines = getsgent getsgnam sgetsgent fgetsgent putsgent \
|
||||||
getsgent_r getsgnam_r sgetsgent_r fgetsgent_r
|
getsgent_r getsgnam_r sgetsgent_r fgetsgent_r
|
||||||
|
|
||||||
tests = tst-gshadow
|
tests = tst-gshadow tst-putsgent
|
||||||
|
|
||||||
CFLAGS-getsgent_r.c = -fexceptions
|
CFLAGS-getsgent_r.c = -fexceptions
|
||||||
CFLAGS-getsgent.c = -fexceptions
|
CFLAGS-getsgent.c = -fexceptions
|
||||||
|
@ -15,9 +15,11 @@
|
|||||||
License along with the GNU C Library; if not, see
|
License along with the GNU C Library; if not, see
|
||||||
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <gshadow.h>
|
#include <gshadow.h>
|
||||||
|
#include <nss.h>
|
||||||
|
|
||||||
#define _S(x) x ? x : ""
|
#define _S(x) x ? x : ""
|
||||||
|
|
||||||
@ -29,6 +31,15 @@ putsgent (const struct sgrp *g, FILE *stream)
|
|||||||
{
|
{
|
||||||
int errors = 0;
|
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);
|
_IO_flockfile (stream);
|
||||||
|
|
||||||
if (fprintf (stream, "%s:%s:", g->sg_namp, _S (g->sg_passwd)) < 0)
|
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>
|
#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,
|
char *__buffer, size_t __buflen,
|
||||||
struct passwd **__result);
|
struct passwd **__result);
|
||||||
|
|
||||||
#include <nss/nss.h>
|
#include <nss.h>
|
||||||
|
|
||||||
struct parser_data;
|
struct parser_data;
|
||||||
extern int _nss_files_parse_pwent (char *line, struct passwd *result,
|
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.
|
# This is the trivial part which goes into libc itself.
|
||||||
routines = nsswitch getnssent getnssent_r digits_dots \
|
routines = nsswitch getnssent getnssent_r digits_dots \
|
||||||
|
valid_field valid_list_field rewrite_field \
|
||||||
$(addsuffix -lookup,$(databases))
|
$(addsuffix -lookup,$(databases))
|
||||||
|
|
||||||
# These are the databases that go through nss dispatch.
|
# These are the databases that go through nss dispatch.
|
||||||
@ -47,8 +48,10 @@ install-bin := getent makedb
|
|||||||
makedb-modules = xmalloc hash-string
|
makedb-modules = xmalloc hash-string
|
||||||
extra-objs += $(makedb-modules:=.o)
|
extra-objs += $(makedb-modules:=.o)
|
||||||
|
|
||||||
|
tests-static = tst-field
|
||||||
tests = test-netdb tst-nss-test1 test-digits-dots \
|
tests = test-netdb tst-nss-test1 test-digits-dots \
|
||||||
tst-nss-getpwent bug17079
|
tst-nss-getpwent bug17079 \
|
||||||
|
$(tests-static)
|
||||||
xtests = bug-erange
|
xtests = bug-erange
|
||||||
|
|
||||||
# Specify rules for the nss_* modules. We have some services.
|
# 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)
|
ifeq ($(build-static-nss),yes)
|
||||||
routines += $(libnss_files-routines)
|
routines += $(libnss_files-routines)
|
||||||
static-only-routines += $(libnss_files-routines)
|
static-only-routines += $(libnss_files-routines)
|
||||||
tests-static = tst-nss-static
|
tests-static += tst-nss-static
|
||||||
tests += $(tests-static)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include ../Rules
|
include ../Rules
|
||||||
|
76
nss/getent.c
76
nss/getent.c
@ -184,20 +184,8 @@ ethers_keys (int number, char *key[])
|
|||||||
static void
|
static void
|
||||||
print_group (struct group *grp)
|
print_group (struct group *grp)
|
||||||
{
|
{
|
||||||
unsigned int i = 0;
|
if (putgrent (grp, stdout) != 0)
|
||||||
|
fprintf (stderr, "error writing group entry: %m\n");
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -241,32 +229,8 @@ group_keys (int number, char *key[])
|
|||||||
static void
|
static void
|
||||||
print_gshadow (struct sgrp *sg)
|
print_gshadow (struct sgrp *sg)
|
||||||
{
|
{
|
||||||
unsigned int i = 0;
|
if (putsgent (sg, stdout) != 0)
|
||||||
|
fprintf (stderr, "error writing gshadow entry: %m\n");
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -603,14 +567,8 @@ networks_keys (int number, char *key[])
|
|||||||
static void
|
static void
|
||||||
print_passwd (struct passwd *pwd)
|
print_passwd (struct passwd *pwd)
|
||||||
{
|
{
|
||||||
printf ("%s:%s:%lu:%lu:%s:%s:%s\n",
|
if (putpwent (pwd, stdout) != 0)
|
||||||
pwd->pw_name ? pwd->pw_name : "",
|
fprintf (stderr, "error writing passwd entry: %m\n");
|
||||||
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 : "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -812,26 +770,8 @@ services_keys (int number, char *key[])
|
|||||||
static void
|
static void
|
||||||
print_shadow (struct spwd *sp)
|
print_shadow (struct spwd *sp)
|
||||||
{
|
{
|
||||||
printf ("%s:%s:",
|
if (putspent (sp, stdout) != 0)
|
||||||
sp->sp_namp ? sp->sp_namp : "",
|
fprintf (stderr, "error writing shadow entry: %m\n");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 getpwnam getpwuid \
|
||||||
getpwent_r getpwnam_r getpwuid_r fgetpwent_r
|
getpwent_r getpwnam_r getpwuid_r fgetpwent_r
|
||||||
|
|
||||||
tests := tst-getpw
|
tests := tst-getpw tst-putpwent
|
||||||
|
|
||||||
include ../Rules
|
include ../Rules
|
||||||
|
|
||||||
|
@ -18,37 +18,47 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <nss.h>
|
||||||
|
|
||||||
#define _S(x) x ?: ""
|
#define _S(x) x ?: ""
|
||||||
|
|
||||||
/* Write an entry to the given stream.
|
/* Write an entry to the given stream. This must know the format of
|
||||||
This must know the format of the password file. */
|
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
|
int
|
||||||
putpwent (p, stream)
|
putpwent (const struct passwd *p, FILE *stream)
|
||||||
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);
|
__set_errno (EINVAL);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->pw_name[0] == '+' || p->pw_name[0] == '-')
|
int ret;
|
||||||
{
|
char *gecos_alloc;
|
||||||
if (fprintf (stream, "%s:%s:::%s:%s:%s\n",
|
const char *gecos = __nss_rewrite_field (p->pw_gecos, &gecos_alloc);
|
||||||
p->pw_name, _S (p->pw_passwd),
|
|
||||||
_S (p->pw_gecos), _S (p->pw_dir), _S (p->pw_shell)) < 0)
|
if (gecos == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
if (p->pw_name[0] == '+' || p->pw_name[0] == '-')
|
||||||
|
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
|
else
|
||||||
{
|
ret = fprintf (stream, "%s:%s:%lu:%lu:%s:%s:%s\n",
|
||||||
if (fprintf (stream, "%s:%s:%lu:%lu:%s:%s:%s\n",
|
|
||||||
p->pw_name, _S (p->pw_passwd),
|
p->pw_name, _S (p->pw_passwd),
|
||||||
(unsigned long int) p->pw_uid,
|
(unsigned long int) p->pw_uid,
|
||||||
(unsigned long int) p->pw_gid,
|
(unsigned long int) p->pw_gid,
|
||||||
_S (p->pw_gecos), _S (p->pw_dir), _S (p->pw_shell)) < 0)
|
gecos, _S (p->pw_dir), _S (p->pw_shell));
|
||||||
return -1;
|
|
||||||
}
|
free (gecos_alloc);
|
||||||
return 0;
|
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 \
|
getspent_r getspnam_r sgetspent_r fgetspent_r \
|
||||||
lckpwdf
|
lckpwdf
|
||||||
|
|
||||||
tests = tst-shadow
|
tests = tst-shadow tst-putspent
|
||||||
|
|
||||||
CFLAGS-getspent_r.c = -fexceptions
|
CFLAGS-getspent_r.c = -fexceptions
|
||||||
CFLAGS-getspent.c = -fexceptions
|
CFLAGS-getspent.c = -fexceptions
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
License along with the GNU C Library; if not, see
|
License along with the GNU C Library; if not, see
|
||||||
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <nss.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <shadow.h>
|
#include <shadow.h>
|
||||||
|
|
||||||
@ -31,6 +33,13 @@ putspent (const struct spwd *p, FILE *stream)
|
|||||||
{
|
{
|
||||||
int errors = 0;
|
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);
|
flockfile (stream);
|
||||||
|
|
||||||
if (fprintf (stream, "%s:%s:", p->sp_namp, _S (p->sp_pwdp)) < 0)
|
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