aboutsummaryrefslogtreecommitdiff
path: root/src/auth.c
blob: 97e934f154e2754a6dbb65bd3af6c5ebaa8110cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * auth.c
 *
 * Created by buzzert <buzzert@buzzert.net> 2019-01-19
 */

#include "auth.h"

#include <pthread.h>
#include <pwd.h>
#include <security/pam_appl.h>
#include <semaphore.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

struct auth_handle_t {
    void             *context;
    auth_callbacks_t  callbacks;

    sem_t                   prompt_semaphore;
    auth_prompt_response_t *prompt_response;
};

int process_message(const struct pam_message *msg, struct pam_response *resp, struct auth_handle_t *handle)
{
    switch (msg->msg_style) {
        case PAM_PROMPT_ECHO_ON:
        case PAM_PROMPT_ECHO_OFF: {
            handle->callbacks.prompt_handler(msg->msg, handle->context);

            sem_wait(&handle->prompt_semaphore);

            auth_prompt_response_t *response = handle->prompt_response;
            resp->resp = response->response_buffer;
            resp->resp_retcode = response->response_code;
            break;
        }
        case PAM_ERROR_MSG:
            handle->callbacks.error_handler(msg->msg, handle->context);
            break;
        case PAM_TEXT_INFO:
            handle->callbacks.info_handler(msg->msg, handle->context);
            break;
    }

    return PAM_SUCCESS;
}

int perform_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *data)
{
	*resp = calloc(num_msg, sizeof(struct pam_response));

    struct auth_handle_t *handle = (struct auth_handle_t *)data;
    for (unsigned i = 0; i < num_msg; ++i) {
        process_message(msg[i], resp[i], handle);
    }

    return PAM_SUCCESS;
}

static void* auth_thread_main(void *arg)
{
    struct pam_conv conv;
    conv.conv = perform_conversation;
    conv.appdata_ptr = arg;

    // Get current username
    struct passwd *pwd = getpwuid(getuid());
    const char *username = pwd->pw_name;
    if (username == NULL || strlen(username) == 0) {
        fprintf(stderr, "Couldn't get name for the current user\n");
        // todo: report to callback
    }

    // Start PAM authentication
    pam_handle_t *pam = NULL;
    pam_start(
        "login",
        username,
        &conv,
        &pam
    );

    bool authenticating = true;
    struct auth_handle_t *handle = (struct auth_handle_t *)arg;
    while (authenticating) {
        int status = pam_authenticate(pam, 0);
        handle->callbacks.result_handler(status, handle->context);

        if (status == PAM_SUCCESS) {
            authenticating = false;
        }
    }

    pam_end(pam, 0);

    return NULL;
}

struct auth_handle_t* auth_begin_authentication(auth_callbacks_t callbacks, void *context)
{
    struct auth_handle_t *handle = malloc(sizeof(struct auth_handle_t));
    handle->callbacks = callbacks;
    handle->context = context;
    handle->prompt_response = NULL;
    sem_init(&handle->prompt_semaphore, 0, 0);

    pthread_t auth_thread;
    if (pthread_create(&auth_thread, NULL, auth_thread_main, handle)) {
        fprintf(stderr, "Error creating auth thread\n");
    }

    return handle;
}


void auth_attempt_authentication(struct auth_handle_t *handle, auth_prompt_response_t response)
{
    if (handle->prompt_response == NULL) {
        handle->prompt_response = malloc(sizeof(auth_prompt_response_t));
    }
    handle->prompt_response = memcpy(handle->prompt_response, &response, sizeof(auth_prompt_response_t));

    sem_post(&handle->prompt_semaphore);
}