diff options
author | Florian Weimer <fweimer@redhat.com> | 2015-10-02 11:34:13 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2015-10-02 11:34:13 +0200 |
commit | 676599b36a92f3c201c5682ee7a5caddd9f370a4 (patch) | |
tree | 6860752c26ccab76ee9db5e60ff465d1edf25feb /pwd/putpwent.c | |
parent | b0f81637d5bda47be93bac34b68f429a12979321 (diff) | |
download | glibc-676599b36a92f3c201c5682ee7a5caddd9f370a4.tar.gz glibc-676599b36a92f3c201c5682ee7a5caddd9f370a4.tar.xz glibc-676599b36a92f3c201c5682ee7a5caddd9f370a4.zip |
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 '+'.
Diffstat (limited to 'pwd/putpwent.c')
-rw-r--r-- | pwd/putpwent.c | 52 |
1 files changed, 31 insertions, 21 deletions
diff --git a/pwd/putpwent.c b/pwd/putpwent.c index 8be58c27ae..16b9c6f80f 100644 --- a/pwd/putpwent.c +++ b/pwd/putpwent.c @@ -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; } |