1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert * Logging functions for the fake PAM library, used for testing.
3*bf6873c5SCy Schubert *
4*bf6873c5SCy Schubert * This file contains the implementation of pam_syslog and pam_vsyslog, which
5*bf6873c5SCy Schubert * log to an internal buffer rather than to syslog, and the testing function
6*bf6873c5SCy Schubert * used to recover that buffer. It also includes the pam_strerror
7*bf6873c5SCy Schubert * implementation.
8*bf6873c5SCy Schubert *
9*bf6873c5SCy Schubert * The canonical version of this file is maintained in the rra-c-util package,
10*bf6873c5SCy Schubert * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
11*bf6873c5SCy Schubert *
12*bf6873c5SCy Schubert * Written by Russ Allbery <eagle@eyrie.org>
13*bf6873c5SCy Schubert * Copyright 2020 Russ Allbery <eagle@eyrie.org>
14*bf6873c5SCy Schubert * Copyright 2010-2012, 2014
15*bf6873c5SCy Schubert * The Board of Trustees of the Leland Stanford Junior University
16*bf6873c5SCy Schubert *
17*bf6873c5SCy Schubert * Permission is hereby granted, free of charge, to any person obtaining a
18*bf6873c5SCy Schubert * copy of this software and associated documentation files (the "Software"),
19*bf6873c5SCy Schubert * to deal in the Software without restriction, including without limitation
20*bf6873c5SCy Schubert * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21*bf6873c5SCy Schubert * and/or sell copies of the Software, and to permit persons to whom the
22*bf6873c5SCy Schubert * Software is furnished to do so, subject to the following conditions:
23*bf6873c5SCy Schubert *
24*bf6873c5SCy Schubert * The above copyright notice and this permission notice shall be included in
25*bf6873c5SCy Schubert * all copies or substantial portions of the Software.
26*bf6873c5SCy Schubert *
27*bf6873c5SCy Schubert * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28*bf6873c5SCy Schubert * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29*bf6873c5SCy Schubert * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30*bf6873c5SCy Schubert * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31*bf6873c5SCy Schubert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32*bf6873c5SCy Schubert * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33*bf6873c5SCy Schubert * DEALINGS IN THE SOFTWARE.
34*bf6873c5SCy Schubert *
35*bf6873c5SCy Schubert * SPDX-License-Identifier: MIT
36*bf6873c5SCy Schubert */
37*bf6873c5SCy Schubert
38*bf6873c5SCy Schubert #include <config.h>
39*bf6873c5SCy Schubert #include <portable/pam.h>
40*bf6873c5SCy Schubert #include <portable/system.h>
41*bf6873c5SCy Schubert
42*bf6873c5SCy Schubert #include <tests/fakepam/internal.h>
43*bf6873c5SCy Schubert #include <tests/fakepam/pam.h>
44*bf6873c5SCy Schubert #include <tests/tap/basic.h>
45*bf6873c5SCy Schubert #include <tests/tap/string.h>
46*bf6873c5SCy Schubert
47*bf6873c5SCy Schubert /* Used for unused parameters to silence gcc warnings. */
48*bf6873c5SCy Schubert #define UNUSED __attribute__((__unused__))
49*bf6873c5SCy Schubert
50*bf6873c5SCy Schubert /* The struct used to accumulate log messages. */
51*bf6873c5SCy Schubert static struct output *messages = NULL;
52*bf6873c5SCy Schubert
53*bf6873c5SCy Schubert
54*bf6873c5SCy Schubert /*
55*bf6873c5SCy Schubert * Allocate a new, empty output struct and call bail if memory allocation
56*bf6873c5SCy Schubert * fails.
57*bf6873c5SCy Schubert */
58*bf6873c5SCy Schubert struct output *
output_new(void)59*bf6873c5SCy Schubert output_new(void)
60*bf6873c5SCy Schubert {
61*bf6873c5SCy Schubert struct output *output;
62*bf6873c5SCy Schubert
63*bf6873c5SCy Schubert output = bmalloc(sizeof(struct output));
64*bf6873c5SCy Schubert output->count = 0;
65*bf6873c5SCy Schubert output->allocated = 1;
66*bf6873c5SCy Schubert output->lines = bmalloc(sizeof(output->lines[0]));
67*bf6873c5SCy Schubert output->lines[0].line = NULL;
68*bf6873c5SCy Schubert return output;
69*bf6873c5SCy Schubert }
70*bf6873c5SCy Schubert
71*bf6873c5SCy Schubert
72*bf6873c5SCy Schubert /*
73*bf6873c5SCy Schubert * Add a new output line to the output struct, resizing the array as
74*bf6873c5SCy Schubert * necessary. Calls bail if memory allocation fails.
75*bf6873c5SCy Schubert */
76*bf6873c5SCy Schubert void
output_add(struct output * output,int priority,const char * string)77*bf6873c5SCy Schubert output_add(struct output *output, int priority, const char *string)
78*bf6873c5SCy Schubert {
79*bf6873c5SCy Schubert size_t next = output->count;
80*bf6873c5SCy Schubert size_t size, n;
81*bf6873c5SCy Schubert
82*bf6873c5SCy Schubert if (output->count == output->allocated) {
83*bf6873c5SCy Schubert n = output->allocated + 1;
84*bf6873c5SCy Schubert size = sizeof(output->lines[0]);
85*bf6873c5SCy Schubert output->lines = breallocarray(output->lines, n, size);
86*bf6873c5SCy Schubert output->allocated = n;
87*bf6873c5SCy Schubert }
88*bf6873c5SCy Schubert output->lines[next].priority = priority;
89*bf6873c5SCy Schubert output->lines[next].line = bstrdup(string);
90*bf6873c5SCy Schubert output->count++;
91*bf6873c5SCy Schubert }
92*bf6873c5SCy Schubert
93*bf6873c5SCy Schubert
94*bf6873c5SCy Schubert /*
95*bf6873c5SCy Schubert * Return the error string associated with the PAM error code. We do this as
96*bf6873c5SCy Schubert * a giant case statement so that we don't assume anything about the error
97*bf6873c5SCy Schubert * codes used by the system PAM library.
98*bf6873c5SCy Schubert */
99*bf6873c5SCy Schubert const char *
pam_strerror(PAM_STRERROR_CONST pam_handle_t * pamh UNUSED,int code)100*bf6873c5SCy Schubert pam_strerror(PAM_STRERROR_CONST pam_handle_t *pamh UNUSED, int code)
101*bf6873c5SCy Schubert {
102*bf6873c5SCy Schubert /* clang-format off */
103*bf6873c5SCy Schubert switch (code) {
104*bf6873c5SCy Schubert case PAM_SUCCESS: return "No error";
105*bf6873c5SCy Schubert case PAM_OPEN_ERR: return "Failure loading service module";
106*bf6873c5SCy Schubert case PAM_SYMBOL_ERR: return "Symbol not found";
107*bf6873c5SCy Schubert case PAM_SERVICE_ERR: return "Error in service module";
108*bf6873c5SCy Schubert case PAM_SYSTEM_ERR: return "System error";
109*bf6873c5SCy Schubert case PAM_BUF_ERR: return "Memory buffer error";
110*bf6873c5SCy Schubert default: return "Unknown error";
111*bf6873c5SCy Schubert }
112*bf6873c5SCy Schubert /* clang-format on */
113*bf6873c5SCy Schubert }
114*bf6873c5SCy Schubert
115*bf6873c5SCy Schubert
116*bf6873c5SCy Schubert /*
117*bf6873c5SCy Schubert * Log a message using variadic arguments. Just a wrapper around
118*bf6873c5SCy Schubert * pam_vsyslog.
119*bf6873c5SCy Schubert */
120*bf6873c5SCy Schubert void
pam_syslog(const pam_handle_t * pamh,int priority,const char * format,...)121*bf6873c5SCy Schubert pam_syslog(const pam_handle_t *pamh, int priority, const char *format, ...)
122*bf6873c5SCy Schubert {
123*bf6873c5SCy Schubert va_list args;
124*bf6873c5SCy Schubert
125*bf6873c5SCy Schubert va_start(args, format);
126*bf6873c5SCy Schubert pam_vsyslog(pamh, priority, format, args);
127*bf6873c5SCy Schubert va_end(args);
128*bf6873c5SCy Schubert }
129*bf6873c5SCy Schubert
130*bf6873c5SCy Schubert
131*bf6873c5SCy Schubert /*
132*bf6873c5SCy Schubert * Log a PAM error message with a given priority. Just appends the priority,
133*bf6873c5SCy Schubert * a space, and the error message, followed by a newline, to the internal
134*bf6873c5SCy Schubert * buffer, allocating new space if needed. Ignore memory allocation failures;
135*bf6873c5SCy Schubert * we have no way of reporting them, but the tests will fail due to missing
136*bf6873c5SCy Schubert * output.
137*bf6873c5SCy Schubert */
138*bf6873c5SCy Schubert void
pam_vsyslog(const pam_handle_t * pamh UNUSED,int priority,const char * format,va_list args)139*bf6873c5SCy Schubert pam_vsyslog(const pam_handle_t *pamh UNUSED, int priority, const char *format,
140*bf6873c5SCy Schubert va_list args)
141*bf6873c5SCy Schubert {
142*bf6873c5SCy Schubert char *message = NULL;
143*bf6873c5SCy Schubert
144*bf6873c5SCy Schubert bvasprintf(&message, format, args);
145*bf6873c5SCy Schubert if (messages == NULL)
146*bf6873c5SCy Schubert messages = output_new();
147*bf6873c5SCy Schubert output_add(messages, priority, message);
148*bf6873c5SCy Schubert free(message);
149*bf6873c5SCy Schubert }
150*bf6873c5SCy Schubert
151*bf6873c5SCy Schubert
152*bf6873c5SCy Schubert /*
153*bf6873c5SCy Schubert * Used by test code. Returns the accumulated messages in an output struct
154*bf6873c5SCy Schubert * and starts a new one. Caller is responsible for freeing with
155*bf6873c5SCy Schubert * pam_output_free.
156*bf6873c5SCy Schubert */
157*bf6873c5SCy Schubert struct output *
pam_output(void)158*bf6873c5SCy Schubert pam_output(void)
159*bf6873c5SCy Schubert {
160*bf6873c5SCy Schubert struct output *output;
161*bf6873c5SCy Schubert
162*bf6873c5SCy Schubert output = messages;
163*bf6873c5SCy Schubert messages = NULL;
164*bf6873c5SCy Schubert return output;
165*bf6873c5SCy Schubert }
166*bf6873c5SCy Schubert
167*bf6873c5SCy Schubert
168*bf6873c5SCy Schubert /*
169*bf6873c5SCy Schubert * Free an output struct.
170*bf6873c5SCy Schubert */
171*bf6873c5SCy Schubert void
pam_output_free(struct output * output)172*bf6873c5SCy Schubert pam_output_free(struct output *output)
173*bf6873c5SCy Schubert {
174*bf6873c5SCy Schubert size_t i;
175*bf6873c5SCy Schubert
176*bf6873c5SCy Schubert if (output == NULL)
177*bf6873c5SCy Schubert return;
178*bf6873c5SCy Schubert for (i = 0; i < output->count; i++)
179*bf6873c5SCy Schubert if (output->lines[i].line != NULL)
180*bf6873c5SCy Schubert free(output->lines[i].line);
181*bf6873c5SCy Schubert free(output->lines);
182*bf6873c5SCy Schubert free(output);
183*bf6873c5SCy Schubert }
184