diff --git a/include/cfg.h b/include/cfg.h index 464eed4..9c7a50a 100644 --- a/include/cfg.h +++ b/include/cfg.h @@ -27,6 +27,7 @@ X(string, password) \ X(string, address) \ X(uint, port) \ + X(bool, enable_pam) \ struct cfg { #define string char* diff --git a/include/pam_auth.h b/include/pam_auth.h new file mode 100644 index 0000000..ef94e44 --- /dev/null +++ b/include/pam_auth.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020 Nicholas Sica + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include + +bool pam_auth(const char* username, const char* password); diff --git a/meson.build b/meson.build index 980552b..3df3337 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ cc = meson.get_compiler('c') libm = cc.find_library('m', required: false) librt = cc.find_library('rt', required: false) +libpam = cc.find_library('pam', required: get_option('pam')) pixman = dependency('pixman-1') gbm = dependency('gbm', required: get_option('screencopy-dmabuf')) @@ -124,6 +125,12 @@ if gbm.found() and not get_option('screencopy-dmabuf').disabled() config.set('ENABLE_SCREENCOPY_DMABUF', true) endif +if libpam.found() + dependencies += libpam + sources += 'src/pam_auth.c' + config.set('ENABLE_PAM', true) +endif + configure_file( output: 'config.h', configuration: config, diff --git a/meson_options.txt b/meson_options.txt index ce80699..2521c27 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,6 @@ option('screencopy-dmabuf', type: 'feature', value: 'disabled', description: 'Enable GPU-side screencopy (experimental)') +option('pam', type: 'feature', value: 'auto', + description: 'Enable PAM authentication') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/src/main.c b/src/main.c index e7ab470..06e539f 100644 --- a/src/main.c +++ b/src/main.c @@ -54,6 +54,10 @@ #include "damage-refinery.h" #include "usdt.h" +#ifdef ENABLE_PAM +#include "pam_auth.h" +#endif + #ifdef ENABLE_SCREENCOPY_DMABUF #include #include @@ -466,12 +470,16 @@ bool on_auth(const char* username, const char* password, void* ud) { struct wayvnc* self = ud; +#ifdef ENABLE_PAM + if (self->cfg.enable_pam) + return pam_auth(username, password); +#endif + if (strcmp(username, self->cfg.username) != 0) return false; if (strcmp(password, self->cfg.password) != 0) return false; - return true; } @@ -719,17 +727,15 @@ int check_cfg_sanity(struct cfg* cfg) log_error("Authentication enabled, but missing private_key_file\n"); rc = -1; } - - if (!cfg->username) { + if (!cfg->username && !cfg->enable_pam) { log_error("Authentication enabled, but missing username\n"); rc = -1; } - if (!cfg->password) { + if (!cfg->password && !cfg->enable_pam) { log_error("Authentication enabled, but missing password\n"); rc = -1; } - return rc; } diff --git a/src/pam_auth.c b/src/pam_auth.c new file mode 100644 index 0000000..af731f2 --- /dev/null +++ b/src/pam_auth.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020 Nicholas Sica + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "pam_auth.h" + +#include +#include +#include + +#include "logging.h" + +struct credentials { + const char* user; + const char* password; +}; + +static int pam_return_pwd(int num_msg, const struct pam_message** msgm, + struct pam_response** response, void* appdata_ptr) +{ + struct credentials* cred = appdata_ptr; + struct pam_response* resp = calloc(sizeof(*response), num_msg); + for (int i = 0; i < num_msg; i++) { + resp[i].resp_retcode = PAM_SUCCESS; + switch(msgm[i]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + resp[i].resp = strdup(cred->password); + break; + default: + goto error; + } + } + + *response = resp; + return PAM_SUCCESS; + +error: + for (int i = 0; i < num_msg; i++) { + free(resp[i].resp); + } + free(resp); + return PAM_CONV_ERR; +} + +bool pam_auth(const char* username, const char* password) +{ + struct credentials cred = { username, password }; + struct pam_conv conv = { &pam_return_pwd, &cred }; + const char* service = "wayvnc"; + pam_handle_t* pamh; + int result = pam_start(service, username, &conv, &pamh); + if (result != PAM_SUCCESS) { + log_error("ERROR: PAM start failed: %s\n", pam_strerror(pamh, result)); + return false; + } + + result = pam_authenticate(pamh, PAM_SILENT|PAM_DISALLOW_NULL_AUTHTOK); + if (result != PAM_SUCCESS) { + log_error("PAM authenticate failed: %s\n", pam_strerror(pamh, result)); + goto error; + } + + result = pam_acct_mgmt(pamh, 0); + if (result != PAM_SUCCESS) { + log_error("PAM account management failed: %s\n", pam_strerror(pamh, result)); + goto error; + } + +error: + pam_end(pamh, result); + return result == PAM_SUCCESS; +} diff --git a/wayvnc.pam b/wayvnc.pam new file mode 100644 index 0000000..9f83dc6 --- /dev/null +++ b/wayvnc.pam @@ -0,0 +1,2 @@ +auth required pam_unix.so nodelay deny=3 unlock_time=600 +account required pam_unix.so nodelay deny=3 unlock_time=600