blob: dd30fe071e92cf51d4e7f555b8a61ef7f3eddc1c [file] [log] [blame]
roman41a11e42022-06-22 09:27:08 +02001/**
2 * @file pam_netconf.c
3 * @author Roman Janota <xjanot04@fit.vutbr.cz>
4 * @brief libnetconf2 Linux PAM test module
5 *
6 * @copyright
7 * Copyright (c) 2022 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#include <security/pam_modules.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21#include "config.h"
22
23#define N_MESSAGES 2
24#define N_REQUESTS 2
25
26/**
27 * @brief Exchange module's messages for user's replies.
28 *
29 * @param[in] pam_h PAM handle.
30 * @param[in] n_messages Number of messages.
31 * @param[in] msg Module's messages for the user.
32 * @param[out] resp User's responses.
33 * @return PAM_SUCCESS on success;
34 * @return PAM error otherwise.
35 */
36static int
37nc_pam_mod_call_clb(pam_handle_t *pam_h, int n_messages, const struct pam_message **msg, struct pam_response **resp)
38{
39 struct pam_conv *conv;
40 int r;
41
42 /* the callback can be accessed through the handle */
43 r = pam_get_item(pam_h, PAM_CONV, (void *) &conv);
44 if (r != PAM_SUCCESS) {
45 return r;
46 }
47 return conv->conv(n_messages, msg, resp, conv->appdata_ptr);
48}
49
50/**
51 * @brief Validate the user's responses.
52 *
53 * @param[in] username Username.
54 * @param[in] reversed_username User's response to the first challenge.
55 * @param[in] eq_ans User's response to the second challenge.
56 * @return PAM_SUCCESS on success;
57 * @return PAM_AUTH_ERR whenever the user's replies are incorrect.
58 */
59static int
60nc_pam_mod_auth(const char *username, char *reversed_username, char *eq_ans)
61{
62 int i, j, r;
63 size_t len;
64 char *buffer;
65
66 len = strlen(reversed_username);
67 buffer = calloc(len + 1, sizeof *buffer);
68 if (!buffer) {
69 fprintf(stderr, "Memory allocation error.\n");
70 return PAM_BUF_ERR;
71 }
72
73 /* reverse the user's response */
74 for (i = len - 1, j = 0; i >= 0; i--) {
75 buffer[j++] = reversed_username[i];
76 }
77 buffer[j] = '\0';
78
79 if (!strcmp(username, buffer) && !strcmp(eq_ans, "2")) {
80 /* it's a match */
81 r = PAM_SUCCESS;
82 } else {
83 r = PAM_AUTH_ERR;
84 }
85
86 free(buffer);
87 return r;
88}
89
90/**
91 * @brief Free the user's responses.
92 *
93 * @param[in] resp Responses.
94 * @param[in] n Number of responses to be freed.
95 */
96static void
97nc_pam_mod_resp_free(struct pam_response *resp, int n)
98{
99 int i;
100
101 if (!resp) {
102 return;
103 }
104
105 for (i = 0; i < n; i++) {
106 free((resp + i)->resp);
107 }
108 free(resp);
109}
110
111/**
112 * @brief Test module's implementation of "auth" service.
113 *
114 * Prepare prompts for the client and decide based on his
115 * answers whether to allow or disallow access.
116 *
117 * @param[in] pam_h PAM handle.
118 * @param[in] flags Flags.
119 * @param[in] argc Count of module options defined in the PAM configuration file.
120 * @param[in] argv Module options.
121 * @return PAM_SUCCESS on success;
122 * @return PAM error otherwise.
123 */
124API int
125pam_sm_authenticate(pam_handle_t *pam_h, int flags, int argc, const char **argv)
126{
127 int r;
128 const char *username;
129 char *reversed_username = NULL, *eq_ans = NULL;
130 struct pam_message echo_msg, no_echo_msg, unexpected_type_msg, info_msg, err_msg;
131 const struct pam_message *msg[N_MESSAGES];
132 struct pam_response *resp = NULL;
133
134 (void) flags;
135 (void) argc;
136 (void) argv;
137
138 /* get the username and if it's not known then the user will be prompted to enter it */
139 r = pam_get_user(pam_h, &username, NULL);
140 if (r != PAM_SUCCESS) {
141 fprintf(stderr, "Unable to get username.\n");
142 r = PAM_AUTHINFO_UNAVAIL;
143 goto cleanup;
144 }
145
146 /* prepare the messages */
147 echo_msg.msg_style = PAM_PROMPT_ECHO_ON;
148 echo_msg.msg = "Enter your username backwards: ";
149 no_echo_msg.msg_style = PAM_PROMPT_ECHO_OFF;
150 no_echo_msg.msg = "Enter the result to 1+1: ";
151 unexpected_type_msg.msg_style = PAM_AUTH_ERR;
152 unexpected_type_msg.msg = "Arbitrary test message";
153 info_msg.msg_style = PAM_TEXT_INFO;
154 info_msg.msg = "Test info message";
155 err_msg.msg_style = PAM_ERROR_MSG;
156 err_msg.msg = "Test error message";
157
158 /* tests */
159 printf("[TEST #1] Too many PAM messages. Output:\n");
160 r = nc_pam_mod_call_clb(pam_h, 500, msg, &resp);
161 if (r == PAM_SUCCESS) {
162 fprintf(stderr, "[TEST #1] Failed.\n");
163 r = PAM_AUTH_ERR;
164 goto cleanup;
165 }
166 printf("[TEST #1] Passed.\n\n");
167
168 printf("[TEST #2] Negative number of PAM messages. Output:\n");
169 r = nc_pam_mod_call_clb(pam_h, -1, msg, &resp);
170 if (r == PAM_SUCCESS) {
171 fprintf(stderr, "[TEST #2] Failed.\n");
172 r = PAM_AUTH_ERR;
173 goto cleanup;
174 }
175 printf("[TEST #2] Passed.\n\n");
176
177 printf("[TEST #3] 0 PAM messages. Output:\n");
178 r = nc_pam_mod_call_clb(pam_h, 0, msg, &resp);
179 if (r == PAM_SUCCESS) {
180 fprintf(stderr, "[TEST #3] Failed.\n");
181 r = PAM_AUTH_ERR;
182 goto cleanup;
183 }
184 printf("[TEST #3] Passed.\n\n");
185
186 printf("[TEST #4] Unexpected message type. Output:\n");
187 msg[0] = &unexpected_type_msg;
188 r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp);
189 if (r == PAM_SUCCESS) {
190 fprintf(stderr, "[TEST #4] Failed.\n");
191 r = PAM_AUTH_ERR;
192 goto cleanup;
193 }
194 printf("[TEST #4] Passed.\n\n");
195
196 printf("[TEST #5] Info and error messages. Output:\n");
197 msg[0] = &info_msg;
198 msg[1] = &err_msg;
199 r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp);
200 if (r == PAM_SUCCESS) {
201 fprintf(stderr, "[TEST #5] Failed.\n");
202 r = PAM_AUTH_ERR;
203 goto cleanup;
204 }
205 printf("[TEST #5] Passed.\n\n");
206
207 printf("[TEST #6] Authentication attempt with an expired token. Output:\n");
208 /* store the correct messages */
209 msg[0] = &echo_msg;
210 msg[1] = &no_echo_msg;
211
212 /* get responses */
213 r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp);
214 if (r != PAM_SUCCESS) {
215 fprintf(stderr, "[TEST #6] Failed.\n");
216 goto cleanup;
217 }
218
219 reversed_username = resp[0].resp;
220 eq_ans = resp[1].resp;
221
222 /* validate the responses */
223 r = nc_pam_mod_auth(username, reversed_username, eq_ans);
224
225 /* not authenticated */
226 if (r != PAM_SUCCESS) {
227 fprintf(stderr, "[TEST #6] Failed.\n");
228 r = PAM_AUTH_ERR;
229 }
230
231cleanup:
232 /* free the responses */
233 nc_pam_mod_resp_free(resp, N_REQUESTS);
234 return r;
235}
236
237/**
238 * @brief Test module's silly implementation of "account" service.
239 *
240 * @param[in] pam_h PAM handle.
241 * @param[in] flags Flags.
242 * @param[in] argc The count of module options defined in the PAM configuration file.
243 * @param[in] argv Module options.
244 * @return PAM_NEW_AUTHTOK_REQD on success;
245 * @return PAM error otherwise.
246 */
247API int
248pam_sm_acct_mgmt(pam_handle_t *pam_h, int flags, int argc, const char *argv[])
249{
250 int r;
251 const void *username;
252
253 (void) flags;
254 (void) argc;
255 (void) argv;
256
257 /* get and check the username */
258 r = pam_get_item(pam_h, PAM_USER, &username);
259 if (r != PAM_SUCCESS) {
260 return r;
261 }
262 if (!strcmp((const char *)username, "test")) {
263 return PAM_NEW_AUTHTOK_REQD;
264 }
265 return PAM_SYSTEM_ERR;
266}
267
268/**
269 * @brief Test module's silly implementation of "password" service.
270 *
271 * @param[in] pam_h PAM handle.
272 * @param[in] flags Flags.
273 * @param[in] argc The count of module options defined in the PAM configuration file.
274 * @param[in] argv Module options.
275 * @return PAM_SUCCESS on success;
276 * @return PAM error otherwise.
277 */
278API int
279pam_sm_chauthtok(pam_handle_t *pam_h, int flags, int argc, const char *argv[])
280{
281 int r;
282 const void *username;
283
284 (void) argc;
285 (void) argv;
286
287 /* the function is called twice, each time with a different flag,
288 * in the first call just check the username if it matches */
289 if (flags & PAM_PRELIM_CHECK) {
290 r = pam_get_item(pam_h, PAM_USER, &username);
291 if (r != PAM_SUCCESS) {
292 return r;
293 }
294 if (!strcmp((const char *)username, "test")) {
295 return PAM_SUCCESS;
296 } else {
297 return PAM_SYSTEM_ERR;
298 }
299
300 /* change the authentication token in the second call */
301 } else if (flags & PAM_UPDATE_AUTHTOK) {
302 r = pam_set_item(pam_h, PAM_AUTHTOK, "test");
303 if (r == PAM_SUCCESS) {
304 printf("[TEST #6] Passed.\n\n");
305 } else {
306 fprintf(stderr, "[TEST #6] Failed.\n");
307 }
308 return r;
309 }
310 return PAM_SYSTEM_ERR;
311}