Created
June 15, 2023 15:45
-
-
Save 3v1n0/7af42c7d6c6a1e70f3e22009913604af to your computer and use it in GitHub Desktop.
PAM module conv tests
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
#include <glib.h> | |
#include <json-glib/json-glib.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <security/pam_appl.h> | |
#include <security/pam_modules.h> | |
/* expected hook */ | |
PAM_EXTERN int | |
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) | |
{ | |
return PAM_SUCCESS; | |
} | |
PAM_EXTERN int | |
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) | |
{ | |
printf("Acct mgmt\n"); | |
return PAM_SUCCESS; | |
} | |
PAM_EXTERN int | |
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, | |
const char **argv) | |
{ | |
return PAM_SUCCESS; | |
} | |
const char * | |
get_module_name (pam_handle_t *pamh) | |
{ | |
const char *module_name; | |
if (pam_get_item (pamh, PAM_SERVICE, (const void **) &module_name) != PAM_SUCCESS) | |
return NULL; | |
return module_name; | |
} | |
static struct pam_response * | |
send_msg(pam_handle_t *pamh, const char *msg, int style) | |
{ | |
const struct pam_message pam_msg = { | |
.msg_style = style, | |
.msg = msg, | |
}; | |
const struct pam_conv *pc; | |
struct pam_response *resp; | |
if (pam_get_item(pamh, PAM_CONV, (const void **) &pc) != PAM_SUCCESS) | |
return NULL; | |
if (!pc || !pc->conv) | |
return NULL; | |
if (pc->conv(1, (const struct pam_message *[]) { &pam_msg }, &resp, | |
pc->appdata_ptr) != PAM_SUCCESS) | |
return NULL; | |
return resp; | |
} | |
static gboolean | |
send_info_impl (pam_handle_t *pamh, const char *info) | |
{ | |
g_autofree struct pam_response *resp = send_msg(pamh, info, PAM_TEXT_INFO); | |
return (resp != NULL); | |
} | |
static gboolean | |
send_info (pam_handle_t *pamh, const char *info) | |
{ | |
g_autofree char *prefixed_info = NULL; | |
prefixed_info = g_strdup_printf ("INFO from %s: %s", get_module_name (pamh), info); | |
return send_info_impl (pamh, prefixed_info); | |
} | |
static gboolean | |
send_error_impl(pam_handle_t *pamh, const char *error) | |
{ | |
g_autofree struct pam_response *resp = send_msg(pamh, error, PAM_ERROR_MSG); | |
return (resp != NULL); | |
} | |
static gboolean | |
send_error (pam_handle_t *pamh, const char *error) | |
{ | |
g_autofree char *prefixed_error = NULL; | |
prefixed_error = g_strdup_printf ("ERROR from %s: %s", get_module_name (pamh), error); | |
return send_error_impl (pamh, prefixed_error); | |
} | |
static JsonObject * | |
module_communicate (pam_handle_t *pamh, JsonObject *data) | |
{ | |
g_autoptr(GError) error = NULL; | |
g_autoptr(JsonParser) parser = NULL; | |
g_autoptr(JsonNode) root = NULL; | |
g_autoptr(JsonGenerator) generator = NULL; | |
g_autofree char *json = NULL; | |
g_autofree struct pam_response *resp = NULL; | |
g_return_val_if_fail (data != NULL, NULL); | |
generator = json_generator_new (); | |
root = json_node_alloc (); | |
json_node_init_object (root, data); | |
json_generator_set_root (generator, root); | |
json = json_generator_to_data (generator, NULL); | |
resp = send_msg (pamh, json, PAM_PROMPT_ECHO_ON); | |
g_return_val_if_fail(resp, NULL); | |
parser = json_parser_new (); | |
if (!json_parser_load_from_data (parser, resp->resp, -1, &error)) | |
{ | |
g_autofree char *msg = g_strdup_printf ("Failed parsing JSON response: %s", | |
error->message); | |
send_error (pamh, msg); | |
return NULL; | |
} | |
if (!JSON_NODE_HOLDS_OBJECT (json_parser_get_root (parser))) | |
{ | |
send_error (pamh, "Not a JSON Object"); | |
return FALSE; | |
} | |
return json_node_dup_object (json_parser_get_root (parser)); | |
} | |
static JsonObject * | |
module_communicate_checked (pam_handle_t *pamh, JsonObject *data) | |
{ | |
g_autoptr(JsonObject) ret_obj = NULL; | |
ret_obj = module_communicate (pamh, data); | |
if (!ret_obj) | |
return NULL; | |
if (json_object_has_member (ret_obj, "error")) | |
{ | |
send_error (pamh, json_object_get_string_member (ret_obj, "error")); | |
return NULL; | |
} | |
if (g_strcmp0 ( | |
json_object_get_string_member_with_default (ret_obj, "status", NULL), | |
"ok") != 0) | |
{ | |
send_error (pamh, "Invalid status"); | |
return NULL; | |
} | |
return g_steal_pointer (&ret_obj); | |
} | |
static JsonObject * | |
module_send_action (pam_handle_t *pamh, const char *action, JsonObject *params) | |
{ | |
g_autoptr(JsonObject) req = NULL; | |
req = json_object_new (); | |
json_object_set_string_member (req, "action", action); | |
if (params) | |
json_object_set_object_member (req, "parameters", params); | |
return module_communicate_checked (pamh, req); | |
} | |
static gboolean | |
module_proto_init (pam_handle_t *pamh) | |
{ | |
g_autoptr(JsonObject) ret = NULL; | |
ret = module_send_action (pamh, "init", NULL); | |
return ret != NULL; | |
} | |
static JsonObject * | |
module_get_data (pam_handle_t *pamh, const char *action, JsonObject *params) | |
{ | |
g_autoptr(JsonObject) ret = NULL; | |
g_autofree char *msg = NULL; | |
ret = module_send_action (pamh, action, params); | |
if (!ret) | |
return NULL; | |
if (!json_object_has_member (ret, "data")) | |
{ | |
send_error (pamh, "Missing data member"); | |
return NULL; | |
} | |
if (!JSON_NODE_HOLDS_OBJECT (json_object_get_member (ret, "data"))) | |
{ | |
send_error (pamh, "Data member not an object"); | |
return NULL; | |
} | |
return json_object_ref (json_object_get_object_member (ret, "data")); | |
} | |
static gboolean | |
module_get_shell_info (pam_handle_t *pamh) | |
{ | |
g_autoptr(JsonObject) data = NULL; | |
g_autofree char *msg = NULL; | |
gint64 width, height; | |
const char *version; | |
data = module_get_data (pamh, "query_info", NULL); | |
if (!data) | |
return FALSE; | |
version = json_object_get_string_member_with_default (data, "version", NULL); | |
if (!version) | |
return FALSE; | |
msg = g_strdup_printf ("Shell version is %s", version); | |
send_info (pamh, msg); | |
g_clear_pointer (&msg, g_free); | |
width = json_object_get_int_member_with_default (data, "width", 0); | |
height = json_object_get_int_member_with_default (data, "height", 0); | |
if (width <= 0 || height <= 0) | |
return FALSE; | |
msg = g_strdup_printf ("Shell resolution is %ldx%ld", width, height); | |
send_info (pamh, msg); | |
return TRUE; | |
} | |
static char * | |
module_start_webview (pam_handle_t *pamh, const char *uri, | |
int preferred_width, int preferred_height, | |
char **allowed_uris, gboolean can_open_popup) | |
{ | |
g_autoptr(JsonObject) data = NULL; | |
g_autoptr(JsonObject) params = NULL; | |
g_autoptr(JsonArray) allowed_uris_array = NULL; | |
g_autofree char *msg = NULL; | |
const char *token; | |
params = json_object_new (); | |
json_object_set_string_member (params, "uri", uri); | |
json_object_set_int_member (params, "preferredWidth", preferred_width); | |
json_object_set_int_member (params, "preferredHeight", preferred_height); | |
json_object_set_boolean_member (params, "canOpenPopup", can_open_popup); | |
allowed_uris_array = json_array_new (); | |
json_object_set_array_member (params, "allowedUris", allowed_uris_array); | |
for (unsigned i = 0; allowed_uris && allowed_uris[i]; ++i) | |
json_array_add_string_element (allowed_uris_array, allowed_uris[i]); | |
data = module_get_data (pamh, "start_webview", params); | |
if (!data) | |
return NULL; | |
token = json_object_get_string_member_with_default (data, "token", NULL); | |
if (!token) | |
{ | |
send_error (pamh, "No token returned!"); | |
return NULL; | |
} | |
return g_strdup (token); | |
} | |
PAM_EXTERN int | |
pam_sm_authenticate(pam_handle_t *pamh, int flags,int argc, const char **argv) | |
{ | |
int retval; | |
const char *user; | |
const char *module_name; | |
g_autofree char *info_msg = NULL; | |
g_autofree char *token = NULL; | |
module_name = get_module_name (pamh); | |
if (!module_name) | |
return PAM_AUTHINFO_UNAVAIL; | |
info_msg = g_strdup_printf("hello module %s", module_name); | |
send_info(pamh, info_msg); | |
g_clear_pointer(&info_msg, g_free); | |
if (strcmp (module_name, "gdm-shell-test") != 0) | |
{ | |
send_error(pamh, "Take another path, this mode is only for GDM!"); | |
return PAM_AUTHINFO_UNAVAIL; | |
} | |
retval = pam_get_item(pamh, PAM_USER, (const void **) &user); | |
if (retval == PAM_SUCCESS && user != NULL) | |
{ | |
info_msg = g_strdup_printf ("Welcome %s", user); | |
send_info (pamh, info_msg); | |
g_clear_pointer (&info_msg, g_free); | |
} | |
else | |
{ | |
send_info(pamh, "Username not set yet, this module can define it later..."); | |
} | |
if (!module_proto_init (pamh)) | |
return PAM_AUTHINFO_UNAVAIL; | |
if (!module_get_shell_info (pamh)) | |
return PAM_AUTHINFO_UNAVAIL; | |
g_usleep (G_USEC_PER_SEC * 0.5); | |
token = module_start_webview (pamh, "https://login.ubuntu.com", | |
300, 400, (char *[]) { | |
"login\\.ubuntu\\.com", | |
"login\\.launchpad\\.net", | |
NULL, | |
}, FALSE); | |
if (token) | |
{ | |
info_msg = g_strdup_printf ("Got Token %s! Sending it to broker...", token); | |
send_info (pamh, info_msg); | |
g_clear_pointer (&info_msg, g_free); | |
g_usleep (G_USEC_PER_SEC); | |
send_info (pamh, "Broker got the ok, let's login!"); | |
g_usleep (G_USEC_PER_SEC * 0.5); | |
return PAM_SUCCESS; | |
} | |
return PAM_AUTH_ERR; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment