first commit
This commit is contained in:
220
pam-module/pam_linux_hello.c
Normal file
220
pam-module/pam_linux_hello.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Linux Hello PAM Module
|
||||
*
|
||||
* PAM module for facial authentication using Linux Hello daemon.
|
||||
* Communicates with the daemon via Unix socket to perform face matching.
|
||||
*
|
||||
* Copyright (C) 2026 Linux Hello Contributors
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_ext.h>
|
||||
|
||||
/* Configuration defaults */
|
||||
#define DEFAULT_TIMEOUT 5
|
||||
#define SOCKET_PATH "/run/linux-hello/auth.sock"
|
||||
#define MAX_MESSAGE_SIZE 4096
|
||||
|
||||
/* Module options */
|
||||
struct module_options {
|
||||
int timeout;
|
||||
int debug;
|
||||
int fallback_password;
|
||||
};
|
||||
|
||||
/* Parse module arguments */
|
||||
static void parse_args(int argc, const char **argv, struct module_options *opts) {
|
||||
int i;
|
||||
|
||||
/* Set defaults */
|
||||
opts->timeout = DEFAULT_TIMEOUT;
|
||||
opts->debug = 0;
|
||||
opts->fallback_password = 0;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (strncmp(argv[i], "timeout=", 8) == 0) {
|
||||
opts->timeout = atoi(argv[i] + 8);
|
||||
if (opts->timeout <= 0) opts->timeout = DEFAULT_TIMEOUT;
|
||||
} else if (strcmp(argv[i], "debug") == 0) {
|
||||
opts->debug = 1;
|
||||
} else if (strcmp(argv[i], "fallback=password") == 0) {
|
||||
opts->fallback_password = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Send authentication request to daemon */
|
||||
static int authenticate_face(pam_handle_t *pamh, const char *user,
|
||||
struct module_options *opts) {
|
||||
int sockfd;
|
||||
struct sockaddr_un addr;
|
||||
char request[MAX_MESSAGE_SIZE];
|
||||
char response[MAX_MESSAGE_SIZE];
|
||||
ssize_t n;
|
||||
int result = PAM_AUTH_ERR;
|
||||
|
||||
/* Create socket */
|
||||
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
if (opts->debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Failed to create socket: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
return PAM_AUTHINFO_UNAVAIL;
|
||||
}
|
||||
|
||||
/* Set socket timeout */
|
||||
struct timeval tv;
|
||||
tv.tv_sec = opts->timeout;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
||||
|
||||
/* Connect to daemon */
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
if (opts->debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Failed to connect to daemon: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
close(sockfd);
|
||||
return PAM_AUTHINFO_UNAVAIL;
|
||||
}
|
||||
|
||||
/* Send authentication request */
|
||||
snprintf(request, sizeof(request), "{\"action\":\"authenticate\",\"user\":\"%s\"}", user);
|
||||
|
||||
n = write(sockfd, request, strlen(request));
|
||||
if (n < 0) {
|
||||
if (opts->debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Failed to send request: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
close(sockfd);
|
||||
return PAM_AUTHINFO_UNAVAIL;
|
||||
}
|
||||
|
||||
/* Read response */
|
||||
n = read(sockfd, response, sizeof(response) - 1);
|
||||
if (n <= 0) {
|
||||
if (opts->debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Failed to read response: %s",
|
||||
n < 0 ? strerror(errno) : "timeout");
|
||||
}
|
||||
close(sockfd);
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
response[n] = '\0';
|
||||
|
||||
/* Parse response (simple check) */
|
||||
if (strstr(response, "\"success\":true") != NULL) {
|
||||
result = PAM_SUCCESS;
|
||||
pam_syslog(pamh, LOG_INFO, "Face authentication successful for %s", user);
|
||||
} else {
|
||||
result = PAM_AUTH_ERR;
|
||||
if (opts->debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Face authentication failed for %s", user);
|
||||
}
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM authentication entry point
|
||||
*/
|
||||
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv) {
|
||||
struct module_options opts;
|
||||
const char *user;
|
||||
int ret;
|
||||
|
||||
/* Parse module arguments */
|
||||
parse_args(argc, argv, &opts);
|
||||
|
||||
/* Get username */
|
||||
ret = pam_get_user(pamh, &user, NULL);
|
||||
if (ret != PAM_SUCCESS || user == NULL || user[0] == '\0') {
|
||||
pam_syslog(pamh, LOG_ERR, "Failed to get username");
|
||||
return PAM_USER_UNKNOWN;
|
||||
}
|
||||
|
||||
if (opts.debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Attempting face authentication for %s", user);
|
||||
}
|
||||
|
||||
/* Attempt face authentication */
|
||||
ret = authenticate_face(pamh, user, &opts);
|
||||
|
||||
/* Handle fallback */
|
||||
if (ret != PAM_SUCCESS && opts.fallback_password) {
|
||||
if (opts.debug) {
|
||||
pam_syslog(pamh, LOG_DEBUG, "Face auth failed, allowing password fallback");
|
||||
}
|
||||
/* Return ignore to let other modules (password) try */
|
||||
return PAM_IGNORE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM credential management (no-op for face auth)
|
||||
*/
|
||||
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv) {
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM account management (no-op)
|
||||
*/
|
||||
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv) {
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* PAM session management (no-op)
|
||||
*/
|
||||
PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv) {
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv) {
|
||||
(void)pamh;
|
||||
(void)flags;
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user