Skip to content

Instantly share code, notes, and snippets.

@3v1n0
Created June 15, 2023 15:45
Show Gist options
  • Save 3v1n0/7af42c7d6c6a1e70f3e22009913604af to your computer and use it in GitHub Desktop.
Save 3v1n0/7af42c7d6c6a1e70f3e22009913604af to your computer and use it in GitHub Desktop.
PAM module conv tests
#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