-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d46b5a6
commit 7d13668
Showing
4 changed files
with
570 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,311 @@ | ||
/** | ||
* @file pam_netconf.c | ||
* @author Roman Janota <[email protected]> | ||
* @brief libnetconf2 Linux PAM test module | ||
* | ||
* @copyright | ||
* Copyright (c) 2022 CESNET, z.s.p.o. | ||
* | ||
* This source code is licensed under BSD 3-Clause License (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
#include <security/pam_modules.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include "config.h" | ||
|
||
#define N_MESSAGES 2 | ||
#define N_REQUESTS 2 | ||
|
||
/** | ||
* @brief Exchange module's messages for user's replies. | ||
* | ||
* @param[in] pam_h PAM handle. | ||
* @param[in] n_messages Number of messages. | ||
* @param[in] msg Module's messages for the user. | ||
* @param[out] resp User's responses. | ||
* @return PAM_SUCCESS on success; | ||
* @return PAM error otherwise. | ||
*/ | ||
static int | ||
nc_pam_mod_call_clb(pam_handle_t *pam_h, int n_messages, const struct pam_message **msg, struct pam_response **resp) | ||
{ | ||
struct pam_conv *conv; | ||
int r; | ||
|
||
/* the callback can be accessed through the handle */ | ||
r = pam_get_item(pam_h, PAM_CONV, (void *) &conv); | ||
if (r != PAM_SUCCESS) { | ||
return r; | ||
} | ||
return conv->conv(n_messages, msg, resp, conv->appdata_ptr); | ||
} | ||
|
||
/** | ||
* @brief Validate the user's responses. | ||
* | ||
* @param[in] username Username. | ||
* @param[in] reversed_username User's response to the first challenge. | ||
* @param[in] eq_ans User's response to the second challenge. | ||
* @return PAM_SUCCESS on success; | ||
* @return PAM_AUTH_ERR whenever the user's replies are incorrect. | ||
*/ | ||
static int | ||
nc_pam_mod_auth(const char *username, char *reversed_username, char *eq_ans) | ||
{ | ||
int i, j, r; | ||
size_t len; | ||
char *buffer; | ||
|
||
len = strlen(reversed_username); | ||
buffer = calloc(len + 1, sizeof *buffer); | ||
if (!buffer) { | ||
fprintf(stderr, "Memory allocation error.\n"); | ||
return PAM_BUF_ERR; | ||
} | ||
|
||
/* reverse the user's response */ | ||
for (i = len - 1, j = 0; i >= 0; i--) { | ||
buffer[j++] = reversed_username[i]; | ||
} | ||
buffer[j] = '\0'; | ||
|
||
if (!strcmp(username, buffer) && !strcmp(eq_ans, "2")) { | ||
/* it's a match */ | ||
r = PAM_SUCCESS; | ||
} else { | ||
r = PAM_AUTH_ERR; | ||
} | ||
|
||
free(buffer); | ||
return r; | ||
} | ||
|
||
/** | ||
* @brief Free the user's responses. | ||
* | ||
* @param[in] resp Responses. | ||
* @param[in] n Number of responses to be freed. | ||
*/ | ||
static void | ||
nc_pam_mod_resp_free(struct pam_response *resp, int n) | ||
{ | ||
int i; | ||
|
||
if (!resp) { | ||
return; | ||
} | ||
|
||
for (i = 0; i < n; i++) { | ||
free((resp + i)->resp); | ||
} | ||
free(resp); | ||
} | ||
|
||
/** | ||
* @brief Test module's implementation of "auth" service. | ||
* | ||
* Prepare prompts for the client and decide based on his | ||
* answers whether to allow or disallow access. | ||
* | ||
* @param[in] pam_h PAM handle. | ||
* @param[in] flags Flags. | ||
* @param[in] argc Count of module options defined in the PAM configuration file. | ||
* @param[in] argv Module options. | ||
* @return PAM_SUCCESS on success; | ||
* @return PAM error otherwise. | ||
*/ | ||
API int | ||
pam_sm_authenticate(pam_handle_t *pam_h, int flags, int argc, const char **argv) | ||
{ | ||
int r; | ||
const char *username; | ||
char *reversed_username = NULL, *eq_ans = NULL; | ||
struct pam_message echo_msg, no_echo_msg, unexpected_type_msg, info_msg, err_msg; | ||
const struct pam_message *msg[N_MESSAGES]; | ||
struct pam_response *resp = NULL; | ||
|
||
(void) flags; | ||
(void) argc; | ||
(void) argv; | ||
|
||
/* get the username and if it's not known then the user will be prompted to enter it */ | ||
r = pam_get_user(pam_h, &username, NULL); | ||
if (r != PAM_SUCCESS) { | ||
fprintf(stderr, "Unable to get username.\n"); | ||
r = PAM_AUTHINFO_UNAVAIL; | ||
goto cleanup; | ||
} | ||
|
||
/* prepare the messages */ | ||
echo_msg.msg_style = PAM_PROMPT_ECHO_ON; | ||
echo_msg.msg = "Enter your username backwards: "; | ||
no_echo_msg.msg_style = PAM_PROMPT_ECHO_OFF; | ||
no_echo_msg.msg = "Enter the result to 1+1: "; | ||
unexpected_type_msg.msg_style = PAM_AUTH_ERR; | ||
unexpected_type_msg.msg = "Arbitrary test message"; | ||
info_msg.msg_style = PAM_TEXT_INFO; | ||
info_msg.msg = "Test info message"; | ||
err_msg.msg_style = PAM_ERROR_MSG; | ||
err_msg.msg = "Test error message"; | ||
|
||
/* tests */ | ||
printf("[TEST #1] Too many PAM messages. Output:\n"); | ||
r = nc_pam_mod_call_clb(pam_h, 500, msg, &resp); | ||
if (r == PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #1] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
goto cleanup; | ||
} | ||
printf("[TEST #1] Passed.\n\n"); | ||
|
||
printf("[TEST #2] Negative number of PAM messages. Output:\n"); | ||
r = nc_pam_mod_call_clb(pam_h, -1, msg, &resp); | ||
if (r == PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #2] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
goto cleanup; | ||
} | ||
printf("[TEST #2] Passed.\n\n"); | ||
|
||
printf("[TEST #3] 0 PAM messages. Output:\n"); | ||
r = nc_pam_mod_call_clb(pam_h, 0, msg, &resp); | ||
if (r == PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #3] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
goto cleanup; | ||
} | ||
printf("[TEST #3] Passed.\n\n"); | ||
|
||
printf("[TEST #4] Unexpected message type. Output:\n"); | ||
msg[0] = &unexpected_type_msg; | ||
r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); | ||
if (r == PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #4] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
goto cleanup; | ||
} | ||
printf("[TEST #4] Passed.\n\n"); | ||
|
||
printf("[TEST #5] Info and error messages. Output:\n"); | ||
msg[0] = &info_msg; | ||
msg[1] = &err_msg; | ||
r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); | ||
if (r == PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #5] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
goto cleanup; | ||
} | ||
printf("[TEST #5] Passed.\n\n"); | ||
|
||
printf("[TEST #6] Authentication attempt with an expired token. Output:\n"); | ||
/* store the correct messages */ | ||
msg[0] = &echo_msg; | ||
msg[1] = &no_echo_msg; | ||
|
||
/* get responses */ | ||
r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); | ||
if (r != PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #6] Failed.\n"); | ||
goto cleanup; | ||
} | ||
|
||
reversed_username = resp[0].resp; | ||
eq_ans = resp[1].resp; | ||
|
||
/* validate the responses */ | ||
r = nc_pam_mod_auth(username, reversed_username, eq_ans); | ||
|
||
/* not authenticated */ | ||
if (r != PAM_SUCCESS) { | ||
fprintf(stderr, "[TEST #6] Failed.\n"); | ||
r = PAM_AUTH_ERR; | ||
} | ||
|
||
cleanup: | ||
/* free the responses */ | ||
nc_pam_mod_resp_free(resp, N_REQUESTS); | ||
return r; | ||
} | ||
|
||
/** | ||
* @brief Test module's silly implementation of "account" service. | ||
* | ||
* @param[in] pam_h PAM handle. | ||
* @param[in] flags Flags. | ||
* @param[in] argc The count of module options defined in the PAM configuration file. | ||
* @param[in] argv Module options. | ||
* @return PAM_NEW_AUTHTOK_REQD on success; | ||
* @return PAM error otherwise. | ||
*/ | ||
API int | ||
pam_sm_acct_mgmt(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) | ||
{ | ||
int r; | ||
const void *username; | ||
|
||
(void) flags; | ||
(void) argc; | ||
(void) argv; | ||
|
||
/* get and check the username */ | ||
r = pam_get_item(pam_h, PAM_USER, &username); | ||
if (r != PAM_SUCCESS) { | ||
return r; | ||
} | ||
if (!strcmp((const char *)username, "test")) { | ||
return PAM_NEW_AUTHTOK_REQD; | ||
} | ||
return PAM_SYSTEM_ERR; | ||
} | ||
|
||
/** | ||
* @brief Test module's silly implementation of "password" service. | ||
* | ||
* @param[in] pam_h PAM handle. | ||
* @param[in] flags Flags. | ||
* @param[in] argc The count of module options defined in the PAM configuration file. | ||
* @param[in] argv Module options. | ||
* @return PAM_SUCCESS on success; | ||
* @return PAM error otherwise. | ||
*/ | ||
API int | ||
pam_sm_chauthtok(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) | ||
{ | ||
int r; | ||
const void *username; | ||
|
||
(void) argc; | ||
(void) argv; | ||
|
||
/* the function is called twice, each time with a different flag, | ||
* in the first call just check the username if it matches */ | ||
if (flags & PAM_PRELIM_CHECK) { | ||
r = pam_get_item(pam_h, PAM_USER, &username); | ||
if (r != PAM_SUCCESS) { | ||
return r; | ||
} | ||
if (!strcmp((const char *)username, "test")) { | ||
return PAM_SUCCESS; | ||
} else { | ||
return PAM_SYSTEM_ERR; | ||
} | ||
|
||
/* change the authentication token in the second call */ | ||
} else if (flags & PAM_UPDATE_AUTHTOK) { | ||
r = pam_set_item(pam_h, PAM_AUTHTOK, "test"); | ||
if (r == PAM_SUCCESS) { | ||
printf("[TEST #6] Passed.\n\n"); | ||
} else { | ||
fprintf(stderr, "[TEST #6] Failed.\n"); | ||
} | ||
return r; | ||
} | ||
return PAM_SYSTEM_ERR; | ||
} |
Oops, something went wrong.