-
Notifications
You must be signed in to change notification settings - Fork 12
/
stns.h
359 lines (342 loc) · 30.2 KB
/
stns.h
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
#ifndef STNS_H
#define STNS_H
#include <curl/curl.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "toml.h"
#include "parson.h"
#include <syslog.h>
#include <nss.h>
#include <grp.h>
#include <pwd.h>
#include <shadow.h>
#include <pthread.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <regex.h>
#define STNS_VERSION "2.0.0"
#define STNS_VERSION_WITH_NAME "stns/" STNS_VERSION
// 10MB
#define STNS_MAX_BUFFER_SIZE (10 * 1024 * 1024)
#define STNS_DEFAULT_BUFFER_SIZE (16 * 1024)
#define STNS_CONFIG_FILE "/etc/stns/client/stns.conf"
#define MAXBUF 1024
#define STNS_LOCK_FILE "/var/tmp/.stns.lock"
#define STNS_HTTP_NOTFOUND 404L
#define STNS_LOCK_RETRY 3
#define STNS_LOCK_INTERVAL_MSEC 10
#define MAX_USERNAME_LENGTH 32
#define MAX_GROUPNAME_LENGTH 32
typedef struct stns_response_t stns_response_t;
struct stns_response_t {
char *data;
size_t size;
long status_code;
};
typedef struct stns_user_httpheader_t stns_user_httpheader_t;
struct stns_user_httpheader_t {
char *key;
char *value;
};
typedef struct stns_user_httpheaders_t stns_user_httpheaders_t;
struct stns_user_httpheaders_t {
stns_user_httpheader_t *headers;
size_t size;
};
typedef struct stns_conf_t stns_conf_t;
struct stns_conf_t {
char *api_endpoint;
char *auth_token;
char *user;
char *password;
char *query_wrapper;
char *chain_ssh_wrapper;
char *http_proxy;
int http_location;
char *cache_dir;
char *tls_cert;
char *tls_key;
char *tls_ca;
int cached_enable;
char *cached_unix_socket;
stns_user_httpheaders_t *http_headers;
int uid_shift;
int gid_shift;
int ssl_verify;
int use_cached;
int request_timeout;
int request_retry;
int request_locktime;
int cache;
int cache_ttl;
int negative_cache_ttl;
};
extern int stns_load_config(char *, stns_conf_t *);
extern void stns_unload_config(stns_conf_t *);
extern int stns_request(stns_conf_t *, char *, stns_response_t *);
extern int stns_request_available(char *, stns_conf_t *);
extern void stns_make_lockfile(char *);
extern int stns_exec_cmd(char *, char *, stns_response_t *);
extern int stns_user_highest_query_available(int);
extern int stns_user_lowest_query_available(int);
extern int stns_group_highest_query_available(int);
extern int stns_group_lowest_query_available(int);
extern int pthread_mutex_retrylock(pthread_mutex_t *mutex);
extern void set_user_highest_id(int);
extern void set_user_lowest_id(int);
extern void set_group_highest_id(int);
extern void set_group_lowest_id(int);
extern int is_valid_username(const char *username);
extern int is_valid_groupname(const char *username);
#define STNS_ENSURE_BY(method_key, key_type, key_name, json_type, json_key, match_method, resource, ltype) \
enum nss_status ensure_##resource##_by_##method_key(char *data, stns_conf_t *c, key_type key_name, \
struct resource *rbuf, char *buf, size_t buflen, int *errnop) \
{ \
int i; \
JSON_Object *leaf; \
JSON_Value *root = json_parse_string(data); \
\
if (root == NULL) { \
syslog(LOG_ERR, "%s(stns)[L%d] json parse error", __func__, __LINE__); \
return NSS_STATUS_UNAVAIL; \
} \
JSON_Array *root_array = json_value_get_array(root); \
for (i = 0; i < json_array_get_count(root_array); i++) { \
leaf = json_array_get_object(root_array, i); \
if (leaf == NULL) { \
continue; \
} \
key_type current = json_object_get_##json_type(leaf, #json_key); \
\
if (match_method) { \
ltype##_ENSURE(leaf); \
json_value_free(root); \
return NSS_STATUS_SUCCESS; \
} \
} \
json_value_free(root); \
\
return NSS_STATUS_NOTFOUND; \
}
#define STNS_SET_DEFAULT_VALUE(buf, name, def) \
char buf[MAXBUF]; \
if (name != NULL && strnlen(name, STNS_MAX_BUFFER_SIZE) > 0) { \
strcpy(buf, name); \
} else { \
strcpy(buf, def); \
} \
name = buf;
#define STNS_GET_SINGLE_VALUE_METHOD(method, first, format, value, resource, query_available, id_shift) \
enum nss_status _nss_stns_##method(first, struct resource *rbuf, char *buf, size_t buflen, int *errnop) \
{ \
int curl_result; \
stns_response_t r; \
stns_conf_t c; \
char url[MAXBUF]; \
\
if (stns_load_config(STNS_CONFIG_FILE, &c) != 0) \
return NSS_STATUS_UNAVAIL; \
query_available; \
snprintf(url, sizeof(url), format, value id_shift); \
r.data = (char *)malloc(STNS_DEFAULT_BUFFER_SIZE); \
curl_result = stns_request(&c, url, &r); \
\
if (curl_result != CURLE_OK) { \
free(r.data); \
stns_unload_config(&c); \
if (r.status_code == STNS_HTTP_NOTFOUND) { \
return NSS_STATUS_NOTFOUND; \
} \
return NSS_STATUS_UNAVAIL; \
} \
\
int result = ensure_##resource##_by_##value(r.data, &c, value, rbuf, buf, buflen, errnop); \
free(r.data); \
stns_unload_config(&c); \
return result; \
}
#define SET_ATTRBUTE(type, name, attr) \
int name##_length = strnlen(name, STNS_MAX_BUFFER_SIZE) + 1; \
\
if (buflen < name##_length) { \
*errnop = ERANGE; \
return NSS_STATUS_TRYAGAIN; \
} \
\
strcpy(buf, name); \
rbuf->type##_##attr = buf; \
buf += name##_length; \
buflen -= name##_length;
#define STNS_SET_ENTRIES(type, ltype, resource, query) \
enum nss_status inner_nss_stns_set##type##ent(char *data, stns_conf_t *c) \
{ \
if (pthread_mutex_retrylock(&type##ent_mutex) != 0) \
return NSS_STATUS_UNAVAIL; \
\
if (entries != NULL || entry_idx != 0) { \
json_value_free(entries); \
} \
entry_idx = 0; \
entries = NULL; \
\
JSON_Value *root = json_parse_string(data); \
if (root == NULL) { \
pthread_mutex_unlock(&type##ent_mutex); \
syslog(LOG_ERR, "%s(stns)[L%d] json parse error", __func__, __LINE__); \
return NSS_STATUS_UNAVAIL; \
} \
\
entries = root; \
\
pthread_mutex_unlock(&type##ent_mutex); \
return NSS_STATUS_SUCCESS; \
} \
\
enum nss_status _nss_stns_set##type##ent(void) \
{ \
int curl_result; \
stns_response_t r; \
stns_conf_t c; \
if (stns_load_config(STNS_CONFIG_FILE, &c) != 0) \
return NSS_STATUS_UNAVAIL; \
\
r.data = (char *)malloc(STNS_DEFAULT_BUFFER_SIZE); \
curl_result = stns_request(&c, #query, &r); \
if (curl_result != CURLE_OK) { \
free(r.data); \
stns_unload_config(&c); \
if (r.status_code == STNS_HTTP_NOTFOUND) { \
return NSS_STATUS_NOTFOUND; \
} \
return NSS_STATUS_UNAVAIL; \
} \
\
int result = inner_nss_stns_set##type##ent(r.data, &c); \
free(r.data); \
stns_unload_config(&c); \
return result; \
} \
\
enum nss_status _nss_stns_end##type##ent(void) \
{ \
if (pthread_mutex_retrylock(&type##ent_mutex) != 0) \
return NSS_STATUS_UNAVAIL; \
if (entries != NULL || entry_idx != 0) { \
json_value_free(entries); \
} \
entry_idx = 0; \
entries = NULL; \
pthread_mutex_unlock(&type##ent_mutex); \
return NSS_STATUS_SUCCESS; \
} \
\
enum nss_status inner_nss_stns_get##type##ent_r(stns_conf_t *c, struct resource *rbuf, char *buf, size_t buflen, \
int *errnop) \
{ \
enum nss_status ret = NSS_STATUS_SUCCESS; \
JSON_Array *array_entries = json_value_get_array(entries); \
\
if (array_entries == NULL) { \
ret = _nss_stns_set##type##ent(); \
} \
\
if (ret != NSS_STATUS_SUCCESS) { \
return ret; \
} \
\
if (entry_idx >= json_array_get_count(array_entries)) { \
*errnop = ENOENT; \
return NSS_STATUS_NOTFOUND; \
} \
\
JSON_Object *user = json_array_get_object(array_entries, entry_idx); \
\
ltype##_ENSURE(user); \
entry_idx++; \
return NSS_STATUS_SUCCESS; \
} \
\
enum nss_status _nss_stns_get##type##ent_r(struct resource *rbuf, char *buf, size_t buflen, int *errnop) \
{ \
stns_conf_t c; \
if (stns_load_config(STNS_CONFIG_FILE, &c) != 0) \
return NSS_STATUS_UNAVAIL; \
if (pthread_mutex_retrylock(&type##ent_mutex) != 0) { \
stns_unload_config(&c); \
return NSS_STATUS_UNAVAIL; \
} \
int result = inner_nss_stns_get##type##ent_r(&c, rbuf, buf, buflen, errnop); \
pthread_mutex_unlock(&type##ent_mutex); \
stns_unload_config(&c); \
return result; \
}
#define SET_GET_HIGH_LOW_ID(highest_or_lowest, user_or_group) \
void set_##user_or_group##_##highest_or_lowest##_id(int id) \
{ \
if (pthread_mutex_retrylock(&user_or_group##_mutex) != 0) \
return; \
highest_or_lowest##_##user_or_group##_id = id; \
pthread_mutex_unlock(&user_or_group##_mutex); \
} \
int get_##user_or_group##_##highest_or_lowest##_id(void) \
{ \
int r; \
if (pthread_mutex_retrylock(&user_or_group##_mutex) != 0) \
return 0; \
r = highest_or_lowest##_##user_or_group##_id; \
pthread_mutex_unlock(&user_or_group##_mutex); \
return r; \
}
#define TOML_STR(m, empty) \
c->m = malloc(strnlen(empty, STNS_MAX_BUFFER_SIZE) + 1); \
strcpy(c->m, empty);
#define TOML_NULL_OR_INT(m, empty) c->m = empty;
#define GET_TOML_BYKEY(m, method, empty, str_or_int) \
if (0 != (raw = toml_raw_in(tab, #m))) { \
if (0 != method(raw, &c->m)) { \
syslog(LOG_ERR, "%s(stns)[L%d] cannot parse toml file:%s key:%s", __func__, __LINE__, filename, #m); \
} \
} else { \
str_or_int(m, empty) \
}
#define GET_TOML_BY_TABLE_KEY(t, m, method, empty, str_or_int) \
if (0 != (in_tab = toml_table_in(tab, #t))) { \
if (0 != (raw = toml_raw_in(in_tab, #m))) { \
if (0 != method(raw, &c->t##_##m)) { \
syslog(LOG_ERR, "%s(stns)[L%d] cannot parse toml file:%s key:%s", __func__, __LINE__, filename, #m); \
} \
} else { \
str_or_int(t##_##m, empty) \
} \
} else { \
str_or_int(t##_##m, empty) \
}
#define UNLOAD_TOML_BYKEY(m) \
if (c->m != NULL) { \
free(c->m); \
}
#define ID_QUERY_AVAILABLE(user_or_group, high_or_low, inequality) \
int stns_##user_or_group##_##high_or_low##est_query_available(int id) \
{ \
int r = get_##user_or_group##_##high_or_low##est_id(); \
if (r != 0 && r inequality id) \
return 0; \
return 1; \
}
#define USER_NAME_QUERY_AVAILABLE \
if (is_valid_username(name) != 0) \
return NSS_STATUS_NOTFOUND;
#define GROUP_NAME_QUERY_AVAILABLE \
if (is_valid_groupname(name) != 0) \
return NSS_STATUS_NOTFOUND;
#define USER_ID_QUERY_AVAILABLE \
if (!stns_user_highest_query_available(uid) || !stns_user_lowest_query_available(uid)) \
return NSS_STATUS_NOTFOUND;
#define GROUP_ID_QUERY_AVAILABLE \
if (!stns_group_highest_query_available(gid) || !stns_group_lowest_query_available(gid)) \
return NSS_STATUS_NOTFOUND;
#endif /* STNS_H */