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