/** * EscalateModuleStart: * @self: PAM module being described in the start message. * @action: Action being called on this module, like * #ESCALATE_MESSAGE_ACTION_AUTHENTICATE when pam_sm_authenticate() is being * used. * @error: (out)(allow-none): Error return location or #NULL. * * Returns: #TRUE if the message was sent. */ gboolean EscalateModuleStart(EscalateModule *self, EscalateMessageAction action, GError **error) { GVariantBuilder *items = NULL; EscalateMessage *message = NULL; gboolean result = FALSE; items = g_variant_builder_new(G_VARIANT_TYPE("a{ims}")); for (guint i = 0; i < G_N_ELEMENTS(escalate_module_include_items); i++) { gint item = escalate_module_include_items[i]; if (!EscalateModuleStartAddItem(self, items, item, error)) { goto done; } } // TODO(vonhollen): Include environment variables? message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_START, action, self->flags, self->username, items); if (EscalateSubprocessSend(self->child, message, error)) result = TRUE; done: if (message) EscalateMessageUnref(message); g_variant_builder_unref(items); return result; }
static void TestNew(gconstpointer user_data) { guint index = GPOINTER_TO_UINT(user_data); GVariantBuilder items; GVariantBuilder env; EscalateMessage *message = NULL; GVariant *expected_values = CreateExampleMessageValues(index); switch (index) { case 0: g_variant_builder_init(&items, G_VARIANT_TYPE_ARRAY); g_variant_builder_add(&items, "{ims}", 2, "testuser"); g_variant_builder_init(&env, G_VARIANT_TYPE_ARRAY); g_variant_builder_add(&env, "{ss}", "PATH", "/path"); message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_START, 1, 0, "testuser", &items, &env); break; case 1: message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_MESSAGE, 1, "Password: "******"testpass", 0); break; case 3: g_variant_builder_init(&env, G_VARIANT_TYPE_ARRAY); g_variant_builder_add(&env, "{ss}", "PATH", "/path"); message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, 0, &env); break; default: g_error("No message available for index %d", index); } g_assert(message); g_assert_cmpint(EscalateMessageGetType(message), ==, example_message_types[index]); g_assert(g_variant_equal(expected_values, message->values)); EscalateMessageUnref(message); g_variant_unref(expected_values); }
/** * EscalateHelperPrompt: * @self: #EscalateHelper instance. * @conv_request: Conversation message to send. * @conv_response: Conversation response contents received. * * Returns: PAM_SUCCESS if one message was sent and response was read, or * PAM_CONV_ERROR on failure. Error are logged with pam_syslog. */ static int EscalateHelperPrompt(EscalateHelper *self, const struct pam_message *conv_request, struct pam_response *conv_response) { EscalateMessage *request = NULL; GError *error = NULL; EscalateMessage *response = NULL; gchar *response_msg = NULL; int response_retcode = 0; int result = PAM_CONV_ERR; request = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_MESSAGE, conv_request->msg_style, conv_request->msg); if (!EscalateMessageWrite(request, self->writer, &error)) { pam_syslog(self->pamh, LOG_WARNING, "Failed to write conversation request: %s", error->message); g_clear_error(&error); goto done; } response = EscalateHelperRecv(self, ESCALATE_MESSAGE_TYPE_CONV_RESPONSE, &error); if (!response) { pam_syslog(self->pamh, LOG_WARNING, "Failed to read conversation response: %s", error->message); g_clear_error(&error); goto done; } EscalateMessageGetValues(response, &response_msg, &response_retcode); if (response_msg) { conv_response->resp = strdup(response_msg); } else { conv_response->resp = NULL; } conv_response->resp_retcode = response_retcode; result = PAM_SUCCESS; done: if (request) EscalateMessageUnref(request); if (response) EscalateMessageUnref(response); g_free(response_msg); return result; }
/** * EscalateHelperDoAction: * @self: #EscalateHelper instance. * @error: (out)(allow-none): Error return location or #NULL. * * Return: #TRUE if the action specified in the start message (just * pam_authenticate for now) was called successfully and the finish message was * sent. */ gboolean EscalateHelperDoAction(EscalateHelper *self, GError **error) { int setcred_result = PAM_SUCCESS; EscalateMessage *message = NULL; GVariantBuilder *env = NULL; gboolean result = FALSE; // Run the action specified in the start message. switch (self->action) { case ESCALATE_MESSAGE_ACTION_AUTHENTICATE: self->result = pam_authenticate(self->pamh, self->flags); if (self->result == PAM_SUCCESS || self->result == PAM_NEW_AUTHTOK_REQD) { // Refresh things like Kerberos credentials. This is safe to do here // even if the client never calls pam_setcred() because the entire auth // stack succeeded. // TODO(vonhollen): Make this configurable by pam_escalate.so. setcred_result = pam_setcred(self->pamh, PAM_REINITIALIZE_CRED); if (setcred_result != PAM_SUCCESS) { pam_syslog(self->pamh, LOG_NOTICE, "pam_setcred() failed for user '%s': %s", self->username, pam_strerror(self->pamh, setcred_result)); } } break; default: self->result = PAM_SYSTEM_ERR; g_error("Unsupported action %d", self->action); } // Prevent this function from being run twice. self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN; // Get the PAM environment to include in the result. env = EscalateUtilPamEnvToVariant(self->pamh, error); if (!env) { goto done; } // Send the final PAM result for the action and the complete environment. message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, self->result, env); result = EscalateMessageWrite(message, self->writer, error); EscalateMessageUnref(message); g_variant_builder_unref(env); done: return result; }
/** * EscalateModuleHandleConversation: * @self: PAM module to forward conversation messages to. * @message: Conversation message from the helper process holding the same * values as one "struct pam_message". * @error: (out)(allow-none): Error return location or #NULL. * * Returns: #TRUE if a response message was sent back to the helper process. */ static gboolean EscalateModuleHandleConversation(EscalateModule *self, EscalateMessage *message, GError **error) { gchar *conv_message_str = NULL; struct pam_message conv_message = { 0, NULL }; const struct pam_message *conv_message_array [] = { &conv_message, NULL }; struct pam_response *conv_response = NULL; gint pam_status = PAM_SYSTEM_ERR; EscalateMessage *response = NULL; gboolean result = FALSE; EscalateMessageGetValues(message, &conv_message.msg_style, &conv_message_str); conv_message.msg = conv_message_str; // TODO(vonhollen): Support multiple requests/responses per call. pam_status = self->conv->conv(1, conv_message_array, &conv_response, self->conv->appdata_ptr); if (pam_status != PAM_SUCCESS) { g_set_error(error, ESCALATE_MODULE_ERROR, ESCALATE_MODULE_ERROR_CONV, "Conversation function failed: %s", pam_strerror(self->pamh, pam_status)); goto done; } response = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_CONV_RESPONSE, conv_response[0].resp, conv_response[0].resp_retcode); if (EscalateSubprocessSend(self->child, response, error)) result = TRUE; done: g_free(conv_message_str); if (conv_response) { free(conv_response[0].resp); free(conv_response); } if (response) { EscalateMessageUnref(response); } return result; }
/** * EscalateHelperDoAction: * @self: #EscalateHelper instance. * @error: (out)(allow-none): Error return location or #NULL. * * Return: #TRUE if the action specified in the start message (just * pam_authenticate for now) was called successfully and the finish message was * sent. */ gboolean EscalateHelperDoAction(EscalateHelper *self, GError **error) { EscalateMessage *message = NULL; gboolean success = FALSE; switch (self->action) { case ESCALATE_MESSAGE_ACTION_AUTHENTICATE: self->result = pam_authenticate(self->pamh, self->flags); break; default: self->result = PAM_SYSTEM_ERR; g_error("Unsupported action %d", self->action); } // Prevents this function from being run twice. self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN; message = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, self->result); success = EscalateMessageWrite(message, self->writer, error); EscalateMessageUnref(message); return success; }
/** * EscalateHelperHandleStart: * @self: #EscalateHelper instance. * @error: (out)(allow-none): Error return location or #NULL. * * Return: #TRUE if pam_start() was called and it's OK to call * EscalateHelperDoAction(). #FALSE if there was an error and the finish message * was sent. */ gboolean EscalateHelperHandleStart(EscalateHelper *self, GError **error) { EscalateMessage *message = NULL; EscalateMessage *response = NULL; GVariantIter *items = NULL; GVariantIter *env = NULL; int pam_status = PAM_SYSTEM_ERR; int item_type = -1; const gchar *item_value = NULL; gboolean result = FALSE; // Support EscalateHelperHandleStart() being called multiple times. self->action = ESCALATE_MESSAGE_ACTION_UNKNOWN; self->flags = 0; g_free(self->username); self->username = NULL; self->result = PAM_SYSTEM_ERR; message = EscalateHelperRecv(self, ESCALATE_MESSAGE_TYPE_START, error); if (!message) goto done; EscalateMessageGetValues(message, &self->action, &self->flags, &self->username, &items, &env); if (!EscalateHelperIsUserAllowed(self, error)) goto done; // TODO(vonhollen): Safely allow calls to multiple services. pam_status = pam_start(ESCALATE_SERVICE_NAME, self->username, &self->conv, &self->pamh); if (pam_status != PAM_SUCCESS) { g_set_error(error, ESCALATE_HELPER_ERROR, ESCALATE_HELPER_ERROR_START_FAILED, "Failed to start PAM session: %s", pam_strerror(self->pamh, pam_status)); goto done; } while (g_variant_iter_loop(items, "{im&s}", &item_type, &item_value)) { if (!EscalateHelperIsSafeItem(item_type)) { g_set_error(error, ESCALATE_HELPER_ERROR, ESCALATE_HELPER_ERROR_UNSUPPORTED_ITEM, "Item type %d is not supported", item_type); goto done; } pam_status = pam_set_item(self->pamh, item_type, item_value); if (pam_status != PAM_SUCCESS) { g_set_error(error, ESCALATE_HELPER_ERROR, ESCALATE_HELPER_ERROR_SET_ITEM_FAILED, "Failed to set item type %d to '%s'", item_type, item_value); goto done; } } result = EscalateUtilPamEnvFromVariant(self->pamh, env, error); done: if (!result) { response = EscalateMessageNew(ESCALATE_MESSAGE_TYPE_FINISH, PAM_SYSTEM_ERR, NULL); EscalateMessageWrite(response, self->writer, NULL); EscalateMessageUnref(response); } if (items) g_variant_iter_free(items); if (env) g_variant_iter_free(env); if (message) EscalateMessageUnref(message); return result; }