1 /*
2 * Copyright (c) 2020 Dmitry V. Levin <ldv@altlinux.org>
3 *
4 * Handy inline functions and macros providing some convenient functionality
5 * to libpam and its modules.
6 */
7
8 #ifndef PAM_INLINE_H
9 #define PAM_INLINE_H
10
11 #include "pam_cc_compat.h"
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16
17 /*
18 * Evaluates to
19 * - a syntax error if the argument is 0,
20 * 0, otherwise.
21 */
22 #define PAM_FAIL_BUILD_ON_ZERO(e_) (sizeof(int[-1 + 2 * !!(e_)]) * 0)
23
24 /*
25 * Evaluates to
26 * 1, if the given type is known to be a non-array type
27 * 0, otherwise.
28 */
29 #define PAM_IS_NOT_ARRAY(a_) PAM_IS_SAME_TYPE((a_), &(a_)[0])
30
31 /*
32 * Evaluates to
33 * - a syntax error if the argument is not an array,
34 * 0, otherwise.
35 */
36 #define PAM_MUST_BE_ARRAY(a_) PAM_FAIL_BUILD_ON_ZERO(!PAM_IS_NOT_ARRAY(a_))
37 /*
38 * Evaluates to
39 * - a syntax error if the argument is an array,
40 * 0, otherwise.
41 */
42 #define PAM_MUST_NOT_BE_ARRAY(a_) PAM_FAIL_BUILD_ON_ZERO(PAM_IS_NOT_ARRAY(a_))
43
44 /* Evaluates to the number of elements in the specified array. */
45 #define PAM_ARRAY_SIZE(a_) (sizeof(a_) / sizeof((a_)[0]) + PAM_MUST_BE_ARRAY(a_))
46
47 /*
48 * Returns NULL if STR does not start with PREFIX,
49 * or a pointer to the first char in STR after PREFIX.
50 * The length of PREFIX is specified by PREFIX_LEN.
51 */
52 static inline const char *
53 pam_str_skip_prefix_len(const char *str, const char *prefix, size_t prefix_len)
54 {
55 return strncmp(str, prefix, prefix_len) ? NULL : str + prefix_len;
56 }
57
58 #define pam_str_skip_prefix(str_, prefix_) \
59 pam_str_skip_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_))
60
61 /*
62 * Returns NULL if STR does not start with PREFIX
63 * (ignoring the case of the characters),
64 * or a pointer to the first char in STR after PREFIX.
65 * The length of PREFIX is specified by PREFIX_LEN.
66 */
67 static inline const char *
68 pam_str_skip_icase_prefix_len(const char *str, const char *prefix, size_t prefix_len)
69 {
70 return strncasecmp(str, prefix, prefix_len) ? NULL : str + prefix_len;
71 }
72
73 #define pam_str_skip_icase_prefix(str_, prefix_) \
74 pam_str_skip_icase_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_))
75
76
77 /*
78 * Macros to securely erase memory
79 */
80
81 #ifdef HAVE_MEMSET_EXPLICIT
82 static inline void pam_overwrite_n(void *ptr, size_t len)
83 {
84 if (ptr)
85 memset_explicit(ptr, len);
86 }
87 #elif defined HAVE_EXPLICIT_BZERO
88 static inline void pam_overwrite_n(void *ptr, size_t len)
89 {
90 if (ptr)
91 explicit_bzero(ptr, len);
92 }
93 #else
94 static inline void pam_overwrite_n(void *ptr, size_t len)
95 {
96 if (ptr) {
97 ptr = memset(ptr, '\0', len);
98 __asm__ __volatile__ ("" : : "r"(ptr) : "memory");
99 }
100 }
101 #endif
102
103 #define pam_overwrite_string(x) \
104 do { \
105 char *xx__ = (x) + PAM_MUST_NOT_BE_ARRAY(x); \
106 if (xx__) \
107 pam_overwrite_n(xx__, strlen(xx__)); \
108 } while(0)
109
110 #define pam_overwrite_array(x) pam_overwrite_n(x, sizeof(x) + PAM_MUST_BE_ARRAY(x))
111
112 #define pam_overwrite_object(x) pam_overwrite_n(x, sizeof(*(x)) + PAM_MUST_NOT_BE_ARRAY(x))
113
114 static inline void
115 pam_drop_response(struct pam_response *reply, int replies)
116 {
117 int reply_i;
118
119 for (reply_i = 0; reply_i < replies; ++reply_i) {
120 if (reply[reply_i].resp) {
121 pam_overwrite_string(reply[reply_i].resp);
122 free(reply[reply_i].resp);
123 }
124 }
125 free(reply);
126 }
127
128
129 static inline int
130 pam_read_passwords(int fd, int npass, char **passwords)
131 {
132 /*
133 * The passwords array must contain npass preallocated
134 * buffers of length PAM_MAX_RESP_SIZE + 1.
135 */
136 int rbytes = 0;
137 int offset = 0;
138 int i = 0;
139 char *pptr;
140 while (npass > 0) {
141 rbytes = read(fd, passwords[i]+offset, PAM_MAX_RESP_SIZE+1-offset);
142
143 if (rbytes < 0) {
144 if (errno == EINTR) {
145 continue;
146 }
147 break;
148 }
149 if (rbytes == 0) {
150 break;
151 }
152
153 while (npass > 0 &&
154 (pptr = memchr(passwords[i] + offset, '\0', rbytes)) != NULL) {
155 ++pptr; /* skip the '\0' */
156 rbytes -= pptr - (passwords[i] + offset);
157 i++;
158 offset = 0;
159 npass--;
160 if (rbytes > 0) {
161 if (npass > 0) {
162 memcpy(passwords[i], pptr, rbytes);
163 }
164 pam_overwrite_n(pptr, rbytes);
165 }
166 }
167 offset += rbytes;
168 }
169
170 /* clear up */
171 if (offset > 0 && npass > 0) {
172 pam_overwrite_n(passwords[i], offset);
173 }
174
175 return i;
176 }
177
178 #endif /* PAM_INLINE_H */