1#define _POSIX_C_SOURCE 200809L
2#include "password-buffer.h"
3#include "log.h"
4#include "swaylock.h"
5#include <stdlib.h>
6#include <errno.h>
7#include <unistd.h>
8#include <limits.h>
9#include <sys/mman.h>
10
11static bool mlock_supported = true;
12static long int page_size = 0;
13
14static long int get_page_size() {
15 if (!page_size) {
16 page_size = sysconf(_SC_PAGESIZE);
17 }
18 return page_size;
19}
20
21// password_buffer_lock expects addr to be page alligned
22static bool password_buffer_lock(char *addr, size_t size) {
23 int retries = 5;
24 while (mlock(addr, size) != 0 && retries > 0) {
25 switch (errno) {
26 case EAGAIN:
27 retries--;
28 if (retries == 0) {
29 swaylock_log(LOG_ERROR, "mlock() supported but failed too often.");
30 return false;
31 }
32 break;
33 case EPERM:
34 swaylock_log_errno(LOG_ERROR, "Unable to mlock() password memory: Unsupported!");
35 mlock_supported = false;
36 return true;
37 default:
38 swaylock_log_errno(LOG_ERROR, "Unable to mlock() password memory.");
39 return false;
40 }
41 return false;
42 }
43
44 return true;
45}
46
47// password_buffer_unlock expects addr to be page alligned
48static bool password_buffer_unlock(char *addr, size_t size) {
49 if (mlock_supported) {
50 if (munlock(addr, size) != 0) {
51 swaylock_log_errno(LOG_ERROR, "Unable to munlock() password memory.");
52 return false;
53 }
54 }
55
56 return true;
57}
58
59char *password_buffer_create(size_t size) {
60 void *buffer;
61 int result = posix_memalign(&buffer, get_page_size(), size);
62 if (result) {
63 //posix_memalign doesn't set errno according to the man page
64 errno = result;
65 swaylock_log_errno(LOG_ERROR, "failed to alloc password buffer");
66 return NULL;
67 }
68
69 if (!password_buffer_lock(buffer, size)) {
70 free(buffer);
71 return NULL;
72 }
73
74 return buffer;
75}
76
77void password_buffer_destroy(char *buffer, size_t size) {
78 clear_buffer(buffer, size);
79 password_buffer_unlock(buffer, size);
80 free(buffer);
81}