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