From patchwork Wed Aug 28 13:29:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 34295 Received: (qmail 97118 invoked by alias); 28 Aug 2019 13:29:09 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 97103 invoked by uid 89); 28 Aug 2019 13:29:08 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.6 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.1 spammy=Audit X-HELO: mx1.redhat.com From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH] elf: Implement DT_AUDIT, DT_DEPAUDIT support [BZ #24943] Date: Wed, 28 Aug 2019 15:29:01 +0200 Message-ID: <874l21ctb6.fsf@oldenburg2.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux) MIME-Version: 1.0 binutils ld has supported --audit, --depaudit for a long time, only support in glibc has been missing. 2019-08-27 Florian Weimer [BZ #24943] elf: Implement DT_AUDIT, DT_DEPAUDIT support. * elf/rtld.c (enum audit_iter_list_state): Define. (struct audit_list_iter): Add state member. (audit_list_iter_init): Initialize state. (audit_list_iter_fetch_index): New function. (audit_list_iter_fetch_string): Likewise. (audit_list_iter_next): Add main_map argument. Call audit_list_iter_fetch_string to fetch more strings. (load_audit_modules): Adjust call to audit_list_iter_next. (dl_main): Call load_audit_modules in more cases. * elf/Makefile (tests): Add tst-audit14, tst-audit15, tst-audit16. (modules-names): Add tst-auditlogmod-1, tst-auditlogmod-2, tst-auditlogmod-3. (LDFLAGS-tst-audit14): Use tst-auditlogmod-1.so as an audit module. (tst-auditlogmod-1.so): Link with libsupport. (tst-audit14.out): Depend on tst-auditlogmod-1.so. (LDFLAGS-tst-audit15) Use tst-auditlogmod-1.so, tst-auditlogmod-2.so as audit modules. (tst-auditlogmod-2.so): Link with libsupport. (tst-audit15.out): Depend on tst-auditlogmod-1.so, tst-auditlogmod-2.so. (LDFLAGS-tst-audit16) Use tst-auditlogmod-1.so, tst-auditlogmod-2.so, tst-auditlogmod-3.so as audit modules. (tst-auditlogmod-3.so): Link with libsupport. (tst-audit16.out): Depend on tst-auditlogmod-1.so, tst-auditlogmod-2.so, tst-auditlogmod-3.so. * elf/tst-audit14.c: New file. * elf/tst-audit15.c: Likewise. * elf/tst-audit16.c: Likewise. * elf/tst-auditlogmod-1.c: Likewise. * elf/tst-auditlogmod-2.c: Likewise. * elf/tst-auditlogmod-3.c: Likewise. * support/xstdio.h (xgetline): Declare. * support/Makefile (libsupport-routines): Add xgetline. * support/xgetline.c: New file. diff --git a/NEWS b/NEWS index a64b89986a..932ac9d9df 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ Major new features: 18661-1:2014 and TS 18661-3:2015 as amended by the resolution of Clarification Request 13 to TS 18661-3. +* The GNU C Library now loads audit modules listed in the DT_AUDIT and + DT_DEPAUDIT dynamic section entries of the main executable. + Deprecated and removed features, and other changes affecting compatibility: * The totalorder and totalordermag functions, and the corresponding diff --git a/elf/Makefile b/elf/Makefile index d470e41402..442e898a97 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -192,7 +192,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \ tst-unwind-ctor tst-unwind-main tst-audit13 \ - tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout + tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout \ + tst-audit14 tst-audit15 tst-audit16 # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ @@ -279,7 +280,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \ tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \ tst-audit13mod1 tst-sonamemove-linkmod1 \ - tst-sonamemove-runmod1 tst-sonamemove-runmod2 + tst-sonamemove-runmod1 tst-sonamemove-runmod2 \ + tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 # Most modules build with _ISOMAC defined, but those filtered out # depend on internal headers. modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\ @@ -1410,6 +1412,22 @@ $(objpfx)tst-audit13.out: $(objpfx)tst-audit13mod1.so LDFLAGS-tst-audit13mod1.so = -Wl,-z,lazy tst-audit13-ENV = LD_AUDIT=$(objpfx)tst-audit13mod1.so +LDFLAGS-tst-audit14 = -Wl,--audit=tst-auditlogmod-1.so +$(objpfx)tst-auditlogmod-1.so: $(libsupport) +$(objpfx)tst-audit14.out: $(objpfx)tst-auditlogmod-1.so +LDFLAGS-tst-audit15 = \ + -Wl,--audit=tst-auditlogmod-1.so,--depaudit=tst-auditlogmod-2.so +$(objpfx)tst-auditlogmod-2.so: $(libsupport) +$(objpfx)tst-audit15.out: \ + $(objpfx)tst-auditlogmod-1.so $(objpfx)tst-auditlogmod-2.so +LDFLAGS-tst-audit16 = \ + -Wl,--audit=tst-auditlogmod-1.so:tst-auditlogmod-2.so \ + -Wl,--depaudit=tst-auditlogmod-3.so +$(objpfx)tst-auditlogmod-3.so: $(libsupport) +$(objpfx)tst-audit16.out: \ + $(objpfx)tst-auditlogmod-1.so $(objpfx)tst-auditlogmod-2.so \ + $(objpfx)tst-auditlogmod-3.so + # tst-sonamemove links against an older implementation of the library. LDFLAGS-tst-sonamemove-linkmod1.so = \ -Wl,--version-script=tst-sonamemove-linkmod1.map \ diff --git a/elf/rtld.c b/elf/rtld.c index c9490ff694..38bc44dc76 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -186,6 +186,15 @@ static struct audit_list struct audit_list *next; } *audit_list; +/* State flag for hte struct audit_list_iter iterator. */ +enum audit_iter_list_state + { + audit_iter_list_in_string, + audit_iter_list_in_dt_audit, + audit_iter_list_in_dt_depaudit, + audit_iter_list_in_list, + }; + /* Iterator for audit_list_string followed by audit_list. */ struct audit_list_iter { @@ -196,6 +205,9 @@ struct audit_list_iter the first element. */ struct audit_list *previous; + /* One of the enum audit_iter_list_state values. */ + unsigned char state; + /* Scratch buffer for returning a name which is part of audit_list_string. */ char fname[SECURE_NAME_LIMIT]; @@ -207,14 +219,66 @@ audit_list_iter_init (struct audit_list_iter *iter) { iter->audit_list_tail = audit_list_string; iter->previous = NULL; + iter->state = audit_iter_list_in_string; +} + +/* Look up the INDEX of a dynamic tag in MAIN_MAP and store it in + ITER->audit_list_tail if it exists. */ +static void +audit_list_iter_fetch_index (struct audit_list_iter *iter, + struct link_map *main_map, size_t index) +{ + if (main_map->l_info[index] != NULL) + iter->audit_list_tail + = ((const char *) D_PTR (main_map, l_info[DT_STRTAB]) + + main_map->l_info[index]->d_un.d_val); +} + +/* Advance ITER->state and put the next string into + ITER->audit_list_tail. */ +static void +audit_list_iter_fetch_string (struct audit_list_iter *iter, + struct link_map *main_map) +{ + /* Advance the state. */ + ++iter->state; + assert (iter->state <= audit_iter_list_in_list); + + /* Default to a missing string. */ + iter->audit_list_tail = NULL; + + /* Determine the next string. */ + switch ((enum audit_iter_list_state) iter->state) + { + case audit_iter_list_in_string: + /* Not possible because state was advanced. */ + __builtin_unreachable (); + case audit_iter_list_in_dt_audit: + audit_list_iter_fetch_index (iter, main_map, ADDRIDX (DT_AUDIT)); + break; + case audit_iter_list_in_dt_depaudit: + audit_list_iter_fetch_index (iter, main_map, ADDRIDX (DT_DEPAUDIT)); + break; + case audit_iter_list_in_list: + /* No DT_* tag left to process. */ + return; + } } /* Iterate through both audit_list_string and audit_list. */ static const char * -audit_list_iter_next (struct audit_list_iter *iter) +audit_list_iter_next (struct audit_list_iter *iter, struct link_map *main_map) { - if (iter->audit_list_tail != NULL) + while (iter->state != audit_iter_list_in_list) { + /* If the current string is missing or exhausted, fetch the next + string. This advances iter->state. */ + if (iter->audit_list_tail == NULL || *iter->audit_list_tail == '\0') + { + audit_list_iter_fetch_string (iter, main_map); + continue; + } + /* First iterate over audit_list_string. */ while (*iter->audit_list_tail != '\0') { @@ -239,7 +303,9 @@ audit_list_iter_next (struct audit_list_iter *iter) return iter->fname; /* Otherwise, wrap around and try the next name. */ } - /* Fall through to the procesing of audit_list. */ + + /* Fetch the next audit string, or fall through to linked list + processing below. */ } if (iter->previous == NULL) @@ -1065,7 +1131,7 @@ load_audit_modules (struct link_map *main_map) while (true) { - const char *name = audit_list_iter_next (&al_iter); + const char *name = audit_list_iter_next (&al_iter, main_map); if (name == NULL) break; load_audit_module (name, &last_audit); @@ -1612,7 +1678,9 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]); /* If we have auditing DSOs to load, do it now. */ bool need_security_init = true; if (__glibc_unlikely (audit_list != NULL) - || __glibc_unlikely (audit_list_string != NULL)) + || __glibc_unlikely (audit_list_string != NULL) + || __glibc_unlikely (main_map->l_info[ADDRIDX (DT_AUDIT)] != NULL) + || __glibc_unlikely (main_map->l_info[ADDRIDX (DT_DEPAUDIT)] != NULL)) { /* Since we start using the auditing DSOs right away we need to initialize the data structures now. */ diff --git a/elf/tst-audit14.c b/elf/tst-audit14.c new file mode 100644 index 0000000000..a8a60d46fc --- /dev/null +++ b/elf/tst-audit14.c @@ -0,0 +1,46 @@ +/* Main program with DT_AUDIT. One audit module. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include + +static int +do_test (void) +{ + /* Verify what the audit module has written. This test assumes that + standard output has been redirected to a regular file. */ + FILE *fp = xfopen ("/dev/stdout", "r"); + + char *buffer = NULL; + size_t buffer_length = 0; + size_t line_length = xgetline (&buffer, &buffer_length, fp); + const char *message = "info: tst-auditlogmod-1.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + /* No more audit module output. */ + line_length = xgetline (&buffer, &buffer_length, fp); + TEST_COMPARE_BLOB ("", 0, buffer, line_length); + + free (buffer); + xfclose (fp); + return 0; +} + +#include diff --git a/elf/tst-audit15.c b/elf/tst-audit15.c new file mode 100644 index 0000000000..4d74c24789 --- /dev/null +++ b/elf/tst-audit15.c @@ -0,0 +1,50 @@ +/* Main program with DT_AUDIT and DT_DEPAUDIT. Two audit modules. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include + +static int +do_test (void) +{ + /* Verify what the audit modules have written. This test assumes + that standard output has been redirected to a regular file. */ + FILE *fp = xfopen ("/dev/stdout", "r"); + + char *buffer = NULL; + size_t buffer_length = 0; + size_t line_length = xgetline (&buffer, &buffer_length, fp); + const char *message = "info: tst-auditlogmod-1.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + line_length = xgetline (&buffer, &buffer_length, fp); + message = "info: tst-auditlogmod-2.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + /* No more audit module output. */ + line_length = xgetline (&buffer, &buffer_length, fp); + TEST_COMPARE_BLOB ("", 0, buffer, line_length); + + free (buffer); + xfclose (fp); + return 0; +} + +#include diff --git a/elf/tst-audit16.c b/elf/tst-audit16.c new file mode 100644 index 0000000000..2e62a0bf7e --- /dev/null +++ b/elf/tst-audit16.c @@ -0,0 +1,54 @@ +/* Main program with DT_AUDIT and DT_DEPAUDIT. Three audit modules. + Copyright (C) 2019 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 + . */ + +#include +#include +#include +#include + +static int +do_test (void) +{ + /* Verify what the audit modules have written. This test assumes + that standard output has been redirected to a regular file. */ + FILE *fp = xfopen ("/dev/stdout", "r"); + + char *buffer = NULL; + size_t buffer_length = 0; + size_t line_length = xgetline (&buffer, &buffer_length, fp); + const char *message = "info: tst-auditlogmod-1.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + line_length = xgetline (&buffer, &buffer_length, fp); + message = "info: tst-auditlogmod-2.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + line_length = xgetline (&buffer, &buffer_length, fp); + message = "info: tst-auditlogmod-3.so loaded\n"; + TEST_COMPARE_BLOB (message, strlen (message), buffer, line_length); + + /* No more audit module output. */ + line_length = xgetline (&buffer, &buffer_length, fp); + TEST_COMPARE_BLOB ("", 0, buffer, line_length); + + free (buffer); + xfclose (fp); + return 0; +} + +#include diff --git a/elf/tst-auditlogmod-1.c b/elf/tst-auditlogmod-1.c new file mode 100644 index 0000000000..c7ae665f6e --- /dev/null +++ b/elf/tst-auditlogmod-1.c @@ -0,0 +1,27 @@ +/* Audit module which logs that it was loaded. Variant 1. + Copyright (C) 2019 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 + . */ + +#include +#include + +unsigned int +la_version (unsigned int v) +{ + write_message ("info: tst-auditlogmod-1.so loaded\n"); + return LAV_CURRENT; +} diff --git a/elf/tst-auditlogmod-2.c b/elf/tst-auditlogmod-2.c new file mode 100644 index 0000000000..7e7ea054f9 --- /dev/null +++ b/elf/tst-auditlogmod-2.c @@ -0,0 +1,27 @@ +/* Audit module which logs that it was loaded. Variant 2. + Copyright (C) 2019 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 + . */ + +#include +#include + +unsigned int +la_version (unsigned int v) +{ + write_message ("info: tst-auditlogmod-2.so loaded\n"); + return LAV_CURRENT; +} diff --git a/elf/tst-auditlogmod-3.c b/elf/tst-auditlogmod-3.c new file mode 100644 index 0000000000..bae6c9b717 --- /dev/null +++ b/elf/tst-auditlogmod-3.c @@ -0,0 +1,27 @@ +/* Audit module which logs that it was loaded. Variant 3. + Copyright (C) 2019 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 + . */ + +#include +#include + +unsigned int +la_version (unsigned int v) +{ + write_message ("info: tst-auditlogmod-3.so loaded\n"); + return LAV_CURRENT; +} diff --git a/support/Makefile b/support/Makefile index ab66913a02..cc3dbf259e 100644 --- a/support/Makefile +++ b/support/Makefile @@ -92,6 +92,7 @@ libsupport-routines = \ xfopen \ xfork \ xftruncate \ + xgetline \ xgetsockname \ xlisten \ xlseek \ diff --git a/support/xgetline.c b/support/xgetline.c new file mode 100644 index 0000000000..a535b22a2d --- /dev/null +++ b/support/xgetline.c @@ -0,0 +1,39 @@ +/* getline with error checking. + Copyright (C) 2019 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 + . */ + +#include +#include + +size_t +xgetline (char **buffer, size_t *length, FILE *fp) +{ + TEST_VERIFY (!ferror (fp)); + ssize_t ret = getline (buffer, length, fp); + if (ferror (fp)) + { + TEST_VERIFY (ret < 0); + FAIL_EXIT1 ("getline: %m"); + } + if (feof (fp)) + { + TEST_VERIFY (ret <= 0); + return 0; + } + TEST_VERIFY (ret > 0); + return ret; +} diff --git a/support/xstdio.h b/support/xstdio.h index 9d4e66dbfd..c7a4a47e67 100644 --- a/support/xstdio.h +++ b/support/xstdio.h @@ -19,6 +19,7 @@ #ifndef SUPPORT_XSTDIO_H #define SUPPORT_XSTDIO_H +#include #include #include @@ -27,6 +28,11 @@ __BEGIN_DECLS FILE *xfopen (const char *path, const char *mode); void xfclose (FILE *); +/* Read a line from FP, using getline. *BUFFER must be NULL, or a + heap-allocated pointer of *LENGTH bytes. Return the number of bytes + in the line if a line was read, or 0 on EOF. */ +size_t xgetline (char **buffer, size_t *length, FILE *fp); + __END_DECLS #endif /* SUPPORT_XSTDIO_H */