No issues found
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <getopt.h>
6 #include <string.h>
7 #include <selinux/selinux.h>
8 #include <sepol/sepol.h>
9 #ifdef USE_NLS
10 #include <locale.h> /* for setlocale() */
11 #include <libintl.h> /* for gettext() */
12 #define _(msgid) gettext (msgid)
13 #else
14 #define _(msgid) (msgid)
15 #endif
16 #ifndef PACKAGE
17 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
18 #endif
19
20 void usage(char *progname)
21 {
22 fprintf(stderr, _("usage: %s [-qi]\n"), progname);
23 exit(1);
24 }
25
26 int main(int argc, char **argv)
27 {
28 int ret, opt, quiet = 0, nargs, init=0, enforce=0;
29
30 #ifdef USE_NLS
31 setlocale(LC_ALL, "");
32 bindtextdomain(PACKAGE, LOCALEDIR);
33 textdomain(PACKAGE);
34 #endif
35
36 while ((opt = getopt(argc, argv, "bqi")) > 0) {
37 switch (opt) {
38 case 'b':
39 fprintf(stderr, "%s: Warning! The -b option is no longer supported, booleans are always preserved across reloads. Continuing...\n",
40 argv[0]);
41 break;
42 case 'q':
43 quiet = 1;
44 sepol_debug(0);
45 break;
46 case 'i':
47 init = 1;
48 break;
49 default:
50 usage(argv[0]);
51 }
52 }
53
54 nargs = argc - optind;
55 if (nargs > 2)
56 usage(argv[0]);
57 if (nargs >= 1 && !quiet) {
58 fprintf(stderr,
59 "%s: Warning! Policy file argument (%s) is no longer supported, installed policy is always loaded. Continuing...\n",
60 argv[0], argv[optind++]);
61 }
62 if (nargs == 2 && ! quiet) {
63 fprintf(stderr,
64 "%s: Warning! Boolean file argument (%s) is no longer supported, installed booleans file is always used. Continuing...\n",
65 argv[0], argv[optind++]);
66 }
67 if (init) {
68 if (is_selinux_enabled() == 1) {
69 /* SELinux is already enabled, we should not do an initial load again */
70 fprintf(stderr,
71 _("%s: Policy is already loaded and initial load requested\n"),
72 argv[0]);
73 exit(2);
74 }
75 ret = selinux_init_load_policy(&enforce);
76 if (ret != 0 ) {
77 if (enforce > 0) {
78 /* SELinux in enforcing mode but load_policy failed */
79 fprintf(stderr,
80 _("%s: Can't load policy and enforcing mode requested: %s\n"),
81 argv[0], strerror(errno));
82 exit(3);
83 }
84 }
85 }
86 else {
87 ret = selinux_mkload_policy(1);
88 }
89 if (ret < 0) {
90 fprintf(stderr, _("%s: Can't load policy: %s\n"),
91 argv[0], strerror(errno));
92 exit(2);
93 }
94 exit(0);
95 }
No issues found
1 /* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */
2
3 /* FLASK */
4
5 /*
6 * Implementation of the hash table type.
7 */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include "hashtab.h"
12
13 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
14 const hashtab_key_t key),
15 int (*keycmp) (hashtab_t h,
16 const hashtab_key_t key1,
17 const hashtab_key_t key2),
18 unsigned int size)
19 {
20
21 hashtab_t p;
22 unsigned int i;
23
24 p = (hashtab_t) malloc(sizeof(hashtab_val_t));
25 if (p == NULL)
26 return p;
27
28 memset(p, 0, sizeof(hashtab_val_t));
29 p->size = size;
30 p->nel = 0;
31 p->hash_value = hash_value;
32 p->keycmp = keycmp;
33 p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
34 if (p->htable == NULL) {
35 free(p);
36 return NULL;
37 }
38 for (i = 0; i < size; i++)
39 p->htable[i] = (hashtab_ptr_t) NULL;
40
41 return p;
42 }
43
44 int hashtab_insert(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum)
45 {
46 int hvalue;
47 hashtab_ptr_t prev, cur, newnode;
48
49 if (!h)
50 return HASHTAB_OVERFLOW;
51
52 hvalue = h->hash_value(h, key);
53 prev = NULL;
54 cur = h->htable[hvalue];
55 while (cur && h->keycmp(h, key, cur->key) > 0) {
56 prev = cur;
57 cur = cur->next;
58 }
59
60 if (cur && (h->keycmp(h, key, cur->key) == 0))
61 return HASHTAB_PRESENT;
62
63 newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
64 if (newnode == NULL)
65 return HASHTAB_OVERFLOW;
66 memset(newnode, 0, sizeof(struct hashtab_node));
67 newnode->key = key;
68 newnode->datum = datum;
69 if (prev) {
70 newnode->next = prev->next;
71 prev->next = newnode;
72 } else {
73 newnode->next = h->htable[hvalue];
74 h->htable[hvalue] = newnode;
75 }
76
77 h->nel++;
78 return HASHTAB_SUCCESS;
79 }
80
81 int hashtab_remove(hashtab_t h, hashtab_key_t key,
82 void (*destroy) (hashtab_key_t k,
83 hashtab_datum_t d, void *args), void *args)
84 {
85 int hvalue;
86 hashtab_ptr_t cur, last;
87
88 if (!h)
89 return HASHTAB_MISSING;
90
91 hvalue = h->hash_value(h, key);
92 last = NULL;
93 cur = h->htable[hvalue];
94 while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
95 last = cur;
96 cur = cur->next;
97 }
98
99 if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
100 return HASHTAB_MISSING;
101
102 if (last == NULL)
103 h->htable[hvalue] = cur->next;
104 else
105 last->next = cur->next;
106
107 if (destroy)
108 destroy(cur->key, cur->datum, args);
109 free(cur);
110 h->nel--;
111 return HASHTAB_SUCCESS;
112 }
113
114 int hashtab_replace(hashtab_t h, hashtab_key_t key, hashtab_datum_t datum,
115 void (*destroy) (hashtab_key_t k,
116 hashtab_datum_t d, void *args), void *args)
117 {
118 int hvalue;
119 hashtab_ptr_t prev, cur, newnode;
120
121 if (!h)
122 return HASHTAB_OVERFLOW;
123
124 hvalue = h->hash_value(h, key);
125 prev = NULL;
126 cur = h->htable[hvalue];
127 while (cur != NULL && h->keycmp(h, key, cur->key) > 0) {
128 prev = cur;
129 cur = cur->next;
130 }
131
132 if (cur && (h->keycmp(h, key, cur->key) == 0)) {
133 if (destroy)
134 destroy(cur->key, cur->datum, args);
135 cur->key = key;
136 cur->datum = datum;
137 } else {
138 newnode = (hashtab_ptr_t) malloc(sizeof(hashtab_node_t));
139 if (newnode == NULL)
140 return HASHTAB_OVERFLOW;
141 memset(newnode, 0, sizeof(struct hashtab_node));
142 newnode->key = key;
143 newnode->datum = datum;
144 if (prev) {
145 newnode->next = prev->next;
146 prev->next = newnode;
147 } else {
148 newnode->next = h->htable[hvalue];
149 h->htable[hvalue] = newnode;
150 }
151 }
152
153 return HASHTAB_SUCCESS;
154 }
155
156 hashtab_datum_t hashtab_search(hashtab_t h, const hashtab_key_t key)
157 {
158
159 int hvalue;
160 hashtab_ptr_t cur;
161
162 if (!h)
163 return NULL;
164
165 hvalue = h->hash_value(h, key);
166 cur = h->htable[hvalue];
167 while (cur != NULL && h->keycmp(h, key, cur->key) > 0)
168 cur = cur->next;
169
170 if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
171 return NULL;
172
173 return cur->datum;
174 }
175
176 void hashtab_destroy(hashtab_t h)
177 {
178 unsigned int i;
179 hashtab_ptr_t cur, temp;
180
181 if (!h)
182 return;
183
184 for (i = 0; i < h->size; i++) {
185 cur = h->htable[i];
186 while (cur != NULL) {
187 temp = cur;
188 cur = cur->next;
189 free(temp);
190 }
191 h->htable[i] = NULL;
192 }
193
194 free(h->htable);
195 h->htable = NULL;
196
197 free(h);
198 }
199
200 int hashtab_map(hashtab_t h,
201 int (*apply) (hashtab_key_t k,
202 hashtab_datum_t d, void *args), void *args)
203 {
204 unsigned int i, ret;
205 hashtab_ptr_t cur;
206
207 if (!h)
208 return HASHTAB_SUCCESS;
209
210 for (i = 0; i < h->size; i++) {
211 cur = h->htable[i];
212 while (cur != NULL) {
213 ret = apply(cur->key, cur->datum, args);
214 if (ret)
215 return ret;
216 cur = cur->next;
217 }
218 }
219 return HASHTAB_SUCCESS;
220 }
221
222 void hashtab_map_remove_on_error(hashtab_t h,
223 int (*apply) (hashtab_key_t k,
224 hashtab_datum_t d,
225 void *args),
226 void (*destroy) (hashtab_key_t k,
227 hashtab_datum_t d,
228 void *args), void *args)
229 {
230 unsigned int i;
231 int ret;
232 hashtab_ptr_t last, cur, temp;
233
234 if (!h)
235 return;
236
237 for (i = 0; i < h->size; i++) {
238 last = NULL;
239 cur = h->htable[i];
240 while (cur != NULL) {
241 ret = apply(cur->key, cur->datum, args);
242 if (ret) {
243 if (last) {
244 last->next = cur->next;
245 } else {
246 h->htable[i] = cur->next;
247 }
248
249 temp = cur;
250 cur = cur->next;
251 if (destroy)
252 destroy(temp->key, temp->datum, args);
253 free(temp);
254 h->nel--;
255 } else {
256 last = cur;
257 cur = cur->next;
258 }
259 }
260 }
261
262 return;
263 }
264
265 void hashtab_hash_eval(hashtab_t h, char *tag)
266 {
267 unsigned int i;
268 int chain_len, slots_used, max_chain_len;
269 hashtab_ptr_t cur;
270
271 slots_used = 0;
272 max_chain_len = 0;
273 for (i = 0; i < h->size; i++) {
274 cur = h->htable[i];
275 if (cur) {
276 slots_used++;
277 chain_len = 0;
278 while (cur) {
279 chain_len++;
280 cur = cur->next;
281 }
282
283 if (chain_len > max_chain_len)
284 max_chain_len = chain_len;
285 }
286 }
287
288 printf
289 ("%s: %d entries and %d/%d buckets used, longest chain length %d\n",
290 tag, h->nel, slots_used, h->size, max_chain_len);
291 }
Location | Tool | Test ID | Issue | |
---|---|---|---|---|
newrole.c:327:0 | cppcheck | useClosedFile | Used file that is not opened. |
1 /************************************************************************
2 *
3 * newrole
4 *
5 * SYNOPSIS:
6 *
7 * This program allows a user to change their SELinux RBAC role and/or
8 * SELinux TE type (domain) in a manner similar to the way the traditional
9 * UNIX su program allows a user to change their identity.
10 *
11 * USAGE:
12 *
13 * newrole [ -r role ] [ -t type ] [ -l level ] [ -V ] [ -- args ]
14 *
15 * BUILD OPTIONS:
16 *
17 * option USE_PAM:
18 *
19 * Set the USE_PAM constant if you want to authenticate users via PAM.
20 * If USE_PAM is not set, users will be authenticated via direct
21 * access to the shadow password file.
22 *
23 * If you decide to use PAM must be told how to handle newrole. A
24 * good rule-of-thumb might be to tell PAM to handle newrole in the
25 * same way it handles su, except that you should remove the pam_rootok.so
26 * entry so that even root must re-authenticate to change roles.
27 *
28 * If you choose not to use PAM, make sure you have a shadow passwd file
29 * in /etc/shadow. You can use a symlink if your shadow passwd file
30 * lives in another directory. Example:
31 * su
32 * cd /etc
33 * ln -s /etc/auth/shadow shadow
34 *
35 * If you decide not to use PAM, you will also have to make newrole
36 * setuid root, so that it can read the shadow passwd file.
37 *
38 *
39 * Authors:
40 * Anthony Colatrella
41 * Tim Fraser
42 * Steve Grubb <sgrubb@redhat.com>
43 * Darrel Goeddel <DGoeddel@trustedcs.com>
44 * Michael Thompson <mcthomps@us.ibm.com>
45 * Dan Walsh <dwalsh@redhat.com>
46 *
47 *************************************************************************/
48
49 #define _GNU_SOURCE
50
51 #if defined(AUDIT_LOG_PRIV) && !defined(USE_AUDIT)
52 #error AUDIT_LOG_PRIV needs the USE_AUDIT option
53 #endif
54 #if defined(NAMESPACE_PRIV) && !defined(USE_PAM)
55 #error NAMESPACE_PRIV needs the USE_PAM option
56 #endif
57
58 #include <stdio.h>
59 #include <stdlib.h> /* for malloc(), realloc(), free() */
60 #include <pwd.h> /* for getpwuid() */
61 #include <ctype.h>
62 #include <sys/types.h> /* to make getuid() and getpwuid() happy */
63 #include <sys/wait.h> /* for wait() */
64 #include <getopt.h> /* for getopt_long() form of getopt() */
65 #include <fcntl.h>
66 #include <string.h>
67 #include <errno.h>
68 #include <selinux/selinux.h> /* for is_selinux_enabled() */
69 #include <selinux/flask.h> /* for SECCLASS_CHR_FILE */
70 #include <selinux/context.h> /* for context-mangling functions */
71 #include <selinux/get_default_type.h>
72 #include <selinux/get_context_list.h> /* for SELINUX_DEFAULTUSER */
73 #include <signal.h>
74 #include <unistd.h> /* for getuid(), exit(), getopt() */
75 #ifdef USE_AUDIT
76 #include <libaudit.h>
77 #endif
78 #if defined(AUDIT_LOG_PRIV) || (NAMESPACE_PRIV)
79 #include <sys/prctl.h>
80 #include <cap-ng.h>
81 #endif
82 #ifdef USE_NLS
83 #include <locale.h> /* for setlocale() */
84 #include <libintl.h> /* for gettext() */
85 #define _(msgid) gettext (msgid)
86 #else
87 #define _(msgid) (msgid)
88 #endif
89 #ifndef PACKAGE
90 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
91 #endif
92
93 #define TRUE 1
94 #define FALSE 0
95
96 /* USAGE_STRING describes the command-line args of this program. */
97 #define USAGE_STRING "USAGE: newrole [ -r role ] [ -t type ] [ -l level ] [ -p ] [ -V ] [ -- args ]"
98
99 #ifdef USE_PAM
100 #define PAM_SERVICE_CONFIG "/etc/selinux/newrole_pam.conf";
101 #endif
102
103 #define DEFAULT_PATH "/usr/bin:/bin"
104 #define DEFAULT_CONTEXT_SIZE 255 /* first guess at context size */
105
106 extern char **environ;
107
108 /**
109 * Construct from the current range and specified desired level a resulting
110 * range. If the specified level is a range, return that. If it is not, then
111 * construct a range with level as the sensitivity and clearance of the current
112 * context.
113 *
114 * newlevel - the level specified on the command line
115 * range - the range in the current context
116 *
117 * Returns malloc'd memory
118 */
119 static char *build_new_range(char *newlevel, const char *range)
120 {
121 char *newrangep = NULL;
122 const char *tmpptr;
123 size_t len;
124
125 /* a missing or empty string */
126 if (!range || !strlen(range) || !newlevel || !strlen(newlevel))
127 return NULL;
128
129 /* if the newlevel is actually a range - just use that */
130 if (strchr(newlevel, '-')) {
131 newrangep = strdup(newlevel);
132 return newrangep;
133 }
134
135 /* look for MLS range in current context */
136 tmpptr = strchr(range, '-');
137 if (tmpptr) {
138 /* we are inserting into a ranged MLS context */
139 len = strlen(newlevel) + 1 + strlen(tmpptr + 1) + 1;
140 newrangep = (char *)malloc(len);
141 if (!newrangep)
142 return NULL;
143 snprintf(newrangep, len, "%s-%s", newlevel, tmpptr + 1);
144 } else {
145 /* we are inserting into a currently non-ranged MLS context */
146 if (!strcmp(newlevel, range)) {
147 newrangep = strdup(range);
148 } else {
149 len = strlen(newlevel) + 1 + strlen(range) + 1;
150 newrangep = (char *)malloc(len);
151 if (!newrangep)
152 return NULL;
153 snprintf(newrangep, len, "%s-%s", newlevel, range);
154 }
155 }
156
157 return newrangep;
158 }
159
160 #ifdef USE_PAM
161
162 /************************************************************************
163 *
164 * All PAM code goes in this section.
165 *
166 ************************************************************************/
167 #include <security/pam_appl.h> /* for PAM functions */
168 #include <security/pam_misc.h> /* for misc_conv PAM utility function */
169
170 char *service_name = "newrole";
171
172 /* authenticate_via_pam()
173 *
174 * in: pw - struct containing data from our user's line in
175 * the passwd file.
176 * out: nothing
177 * return: value condition
178 * ----- ---------
179 * 1 PAM thinks that the user authenticated themselves properly
180 * 0 otherwise
181 *
182 * This function uses PAM to authenticate the user running this
183 * program. This is the only function in this program that makes PAM
184 * calls.
185 */
186 int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle)
187 {
188
189 int result = 0; /* set to 0 (not authenticated) by default */
190 int pam_rc; /* pam return code */
191 const char *tty_name;
192
193 if (ttyn) {
194 if (strncmp(ttyn, "/dev/", 5) == 0)
195 tty_name = ttyn + 5;
196 else
197 tty_name = ttyn;
198
199 pam_rc = pam_set_item(pam_handle, PAM_TTY, tty_name);
200 if (pam_rc != PAM_SUCCESS) {
201 fprintf(stderr, _("failed to set PAM_TTY\n"));
202 goto out;
203 }
204 }
205
206 /* Ask PAM to authenticate the user running this program */
207 pam_rc = pam_authenticate(pam_handle, 0);
208 if (pam_rc != PAM_SUCCESS) {
209 goto out;
210 }
211
212 /* Ask PAM to verify acct_mgmt */
213 pam_rc = pam_acct_mgmt(pam_handle, 0);
214 if (pam_rc == PAM_SUCCESS) {
215 result = 1; /* user authenticated OK! */
216 }
217
218 out:
219 return result;
220 } /* authenticate_via_pam() */
221
222 #include "hashtab.h"
223
224 static int free_hashtab_entry(hashtab_key_t key, hashtab_datum_t d,
225 void *args __attribute__ ((unused)))
226 {
227 free(key);
228 free(d);
229 return 0;
230 }
231
232 static unsigned int reqsymhash(hashtab_t h, hashtab_key_t key)
233 {
234 char *p, *keyp;
235 size_t size;
236 unsigned int val;
237
238 val = 0;
239 keyp = (char *)key;
240 size = strlen(keyp);
241 for (p = keyp; ((size_t) (p - keyp)) < size; p++)
242 val =
243 (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
244 return val & (h->size - 1);
245 }
246
247 static int reqsymcmp(hashtab_t h
248 __attribute__ ((unused)), hashtab_key_t key1,
249 hashtab_key_t key2)
250 {
251 char *keyp1, *keyp2;
252
253 keyp1 = (char *)key1;
254 keyp2 = (char *)key2;
255 return strcmp(keyp1, keyp2);
256 }
257
258 static hashtab_t app_service_names = NULL;
259 #define PAM_SERVICE_SLOTS 64
260
261 static int process_pam_config(FILE * cfg)
262 {
263 const char *config_file_path = PAM_SERVICE_CONFIG;
264 char *line_buf = NULL;
265 unsigned long lineno = 0;
266 size_t len = 0;
267 char *app = NULL;
268 char *service = NULL;
269 int ret;
270
271 while (getline(&line_buf, &len, cfg) > 0) {
272 char *buffer = line_buf;
273 lineno++;
274 while (isspace(*buffer))
275 buffer++;
276 if (buffer[0] == '#')
277 continue;
278 if (buffer[0] == '\n' || buffer[0] == '\0')
279 continue;
280
281 app = service = NULL;
282 ret = sscanf(buffer, "%as %as\n", &app, &service);
283 if (ret < 2 || !app || !service)
284 goto err;
285
286 ret = hashtab_insert(app_service_names, app, service);
287 if (ret == HASHTAB_OVERFLOW) {
288 fprintf(stderr,
289 _
290 ("newrole: service name configuration hashtable overflow\n"));
291 goto err;
292 }
293 }
294
295 free(line_buf);
296 return 0;
297 err:
298 free(app);
299 free(service);
300 fprintf(stderr, _("newrole: %s: error on line %lu.\n"),
301 config_file_path, lineno);
302 free(line_buf);
303 return -1;
304 }
305
306 /*
307 * Read config file ignoring comment lines.
308 * Files specified one per line executable with a corresponding
309 * pam service name.
310 */
311 static int read_pam_config()
312 {
313 const char *config_file_path = PAM_SERVICE_CONFIG;
314 FILE *cfg = NULL;
315 cfg = fopen(config_file_path, "r");
316 if (!cfg)
317 return 0; /* This configuration is optional. */
318 app_service_names =
319 hashtab_create(reqsymhash, reqsymcmp, PAM_SERVICE_SLOTS);
320 if (!app_service_names)
321 goto err;
322 if (process_pam_config(cfg))
323 goto err;
324 fclose(cfg);
325 return 0;
326 err:
327 fclose(cfg);
(emitted by cppcheck) 328 return -1;
329 }
330
331 #else /* else !USE_PAM */
332
333 /************************************************************************
334 *
335 * All shadow passwd code goes in this section.
336 *
337 ************************************************************************/
338 #include <shadow.h> /* for shadow passwd functions */
339 #include <string.h> /* for strlen(), memset() */
340
341 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
342
343 /* authenticate_via_shadow_passwd()
344 *
345 * in: uname - the calling user's user name
346 * out: nothing
347 * return: value condition
348 * ----- ---------
349 * 1 user authenticated themselves properly according to the
350 * shadow passwd file.
351 * 0 otherwise
352 *
353 * This function uses the shadow passwd file to thenticate the user running
354 * this program.
355 */
356 int authenticate_via_shadow_passwd(const char *uname)
357 {
358 struct spwd *p_shadow_line;
359 char *unencrypted_password_s;
360 char *encrypted_password_s;
361
362 setspent();
363 p_shadow_line = getspnam(uname);
364 endspent();
365 if (!(p_shadow_line)) {
366 fprintf(stderr, _("Cannot find your entry in the shadow "
367 "passwd file.\n"));
368 return 0;
369 }
370
371 /* Ask user to input unencrypted password */
372 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
373 fprintf(stderr, _("getpass cannot open /dev/tty\n"));
374 return 0;
375 }
376
377 /* Use crypt() to encrypt user's input password. */
378 encrypted_password_s = crypt(unencrypted_password_s,
379 p_shadow_line->sp_pwdp);
380 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
381 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
382 }
383 #endif /* if/else USE_PAM */
384
385 /**
386 * This function checks to see if the shell is known in /etc/shells.
387 * If so, it returns 1. On error or illegal shell, it returns 0.
388 */
389 static int verify_shell(const char *shell_name)
390 {
391 int found = 0;
392 const char *buf;
393
394 if (!(shell_name && shell_name[0]))
395 return found;
396
397 while ((buf = getusershell()) != NULL) {
398 /* ignore comments */
399 if (*buf == '#')
400 continue;
401
402 /* check the shell skipping newline char */
403 if (!strcmp(shell_name, buf)) {
404 found = 1;
405 break;
406 }
407 }
408 endusershell();
409 return found;
410 }
411
412 /**
413 * Determine the Linux user identity to re-authenticate.
414 * If supported and set, use the login uid, as this should be more stable.
415 * Otherwise, use the real uid.
416 *
417 * This function assigns malloc'd memory into the pw_copy struct.
418 * Returns zero on success, non-zero otherwise
419 */
420 int extract_pw_data(struct passwd *pw_copy)
421 {
422 uid_t uid;
423 struct passwd *pw;
424
425 #ifdef USE_AUDIT
426 uid = audit_getloginuid();
427 if (uid == (uid_t) - 1)
428 uid = getuid();
429 #else
430 uid = getuid();
431 #endif
432
433 setpwent();
434 pw = getpwuid(uid);
435 endpwent();
436 if (!(pw && pw->pw_name && pw->pw_name[0] && pw->pw_shell
437 && pw->pw_shell[0] && pw->pw_dir && pw->pw_dir[0])) {
438 fprintf(stderr,
439 _("cannot find valid entry in the passwd file.\n"));
440 return -1;
441 }
442
443 *pw_copy = *pw;
444 pw = pw_copy;
445 pw->pw_name = strdup(pw->pw_name);
446 pw->pw_dir = strdup(pw->pw_dir);
447 pw->pw_shell = strdup(pw->pw_shell);
448
449 if (!(pw->pw_name && pw->pw_dir && pw->pw_shell)) {
450 fprintf(stderr, _("Out of memory!\n"));
451 goto out_free;
452 }
453
454 if (verify_shell(pw->pw_shell) == 0) {
455 fprintf(stderr, _("Error! Shell is not valid.\n"));
456 goto out_free;
457 }
458 return 0;
459
460 out_free:
461 free(pw->pw_name);
462 free(pw->pw_dir);
463 free(pw->pw_shell);
464 return -1;
465 }
466
467 /**
468 * Either restore the original environment, or set up a minimal one.
469 *
470 * The minimal environment contains:
471 * TERM, DISPLAY and XAUTHORITY - if they are set, preserve values
472 * HOME, SHELL, USER and LOGNAME - set to contents of /etc/passwd
473 * PATH - set to default value DEFAULT_PATH
474 *
475 * Returns zero on success, non-zero otherwise
476 */
477 static int restore_environment(int preserve_environment,
478 char **old_environ, const struct passwd *pw)
479 {
480 char const *term_env;
481 char const *display_env;
482 char const *xauthority_env;
483 char *term = NULL; /* temporary container */
484 char *display = NULL; /* temporary container */
485 char *xauthority = NULL; /* temporary container */
486 int rc;
487
488 environ = old_environ;
489
490 if (preserve_environment)
491 return 0;
492
493 term_env = getenv("TERM");
494 display_env = getenv("DISPLAY");
495 xauthority_env = getenv("XAUTHORITY");
496
497 /* Save the variable values we want */
498 if (term_env)
499 term = strdup(term_env);
500 if (display_env)
501 display = strdup(display_env);
502 if (xauthority_env)
503 xauthority = strdup(xauthority_env);
504 if ((term_env && !term) || (display_env && !display) ||
505 (xauthority_env && !xauthority)) {
506 rc = -1;
507 goto out;
508 }
509
510 /* Construct a new environment */
511 if ((rc = clearenv())) {
512 fprintf(stderr, _("Unable to clear environment\n"));
513 goto out;
514 }
515
516 /* Restore that which we saved */
517 if (term)
518 rc |= setenv("TERM", term, 1);
519 if (display)
520 rc |= setenv("DISPLAY", display, 1);
521 if (xauthority)
522 rc |= setenv("XAUTHORITY", xauthority, 1);
523 rc |= setenv("HOME", pw->pw_dir, 1);
524 rc |= setenv("SHELL", pw->pw_shell, 1);
525 rc |= setenv("USER", pw->pw_name, 1);
526 rc |= setenv("LOGNAME", pw->pw_name, 1);
527 rc |= setenv("PATH", DEFAULT_PATH, 1);
528 out:
529 free(term);
530 free(display);
531 free(xauthority);
532 return rc;
533 }
534
535 /**
536 * This function will drop the capabilities so that we are left
537 * only with access to the audit system. If the user is root, we leave
538 * the capabilities alone since they already should have access to the
539 * audit netlink socket.
540 *
541 * Returns zero on success, non-zero otherwise
542 */
543 #if defined(AUDIT_LOG_PRIV) && !defined(NAMESPACE_PRIV)
544 static int drop_capabilities(int full)
545 {
546 uid_t uid = getuid();
547 if (!uid) return 0;
548
549 capng_setpid(getpid());
550 capng_clear(CAPNG_SELECT_BOTH);
551 if (capng_lock() < 0)
552 return -1;
553
554 /* Change uid */
555 if (setresuid(uid, uid, uid)) {
556 fprintf(stderr, _("Error changing uid, aborting.\n"));
557 return -1;
558 }
559 if (! full)
560 capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE);
561 return capng_apply(CAPNG_SELECT_BOTH);
562 }
563 #elif defined(NAMESPACE_PRIV)
564 /**
565 * This function will drop the capabilities so that we are left
566 * only with access to the audit system and the ability to raise
567 * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN,
568 * before invoking pam_namespace. These capabilities are needed
569 * for performing bind mounts/unmounts and to create potential new
570 * instance directories with appropriate DAC attributes. If the
571 * user is root, we leave the capabilities alone since they already
572 * should have access to the audit netlink socket and should have
573 * the ability to create/mount/unmount instance directories.
574 *
575 * Returns zero on success, non-zero otherwise
576 */
577 static int drop_capabilities(int full)
578 {
579 capng_setpid(getpid());
580 capng_clear(CAPNG_SELECT_BOTH);
581 if (capng_lock() < 0)
582 return -1;
583
584 uid_t uid = getuid();
585 /* Change uid */
586 if (setresuid(uid, uid, uid)) {
587 fprintf(stderr, _("Error changing uid, aborting.\n"));
588 return -1;
589 }
590 if (! full)
591 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN , CAP_FOWNER , CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_SETPCAP, -1);
592 return capng_apply(CAPNG_SELECT_BOTH);
593 }
594
595 #else
596 static inline int drop_capabilities(__attribute__ ((__unused__)) int full)
597 {
598 return 0;
599 }
600 #endif
601
602 #ifdef NAMESPACE_PRIV
603 /**
604 * This function will set the uid values to be that of caller's uid, and
605 * will drop any privilages which maybe have been raised.
606 */
607 static int transition_to_caller_uid()
608 {
609 uid_t uid = getuid();
610
611 if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
612 fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
613 return -1;
614 }
615
616 if (setresuid(uid, uid, uid)) {
617 fprintf(stderr, _("Error changing uid, aborting.\n"));
618 return -1;
619 }
620 return 0;
621 }
622 #endif
623
624 #ifdef AUDIT_LOG_PRIV
625 /* Send audit message */
626 static
627 int send_audit_message(int success, security_context_t old_context,
628 security_context_t new_context, const char *ttyn)
629 {
630 char *msg = NULL;
631 int rc;
632 int audit_fd = audit_open();
633
634 if (audit_fd < 0) {
635 fprintf(stderr, _("Error connecting to audit system.\n"));
636 return -1;
637 }
638 if (asprintf(&msg, "newrole: old-context=%s new-context=%s",
639 old_context ? old_context : "?",
640 new_context ? new_context : "?") < 0) {
641 fprintf(stderr, _("Error allocating memory.\n"));
642 rc = -1;
643 goto out;
644 }
645 rc = audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
646 msg, NULL, NULL, ttyn, success);
647 if (rc <= 0) {
648 fprintf(stderr, _("Error sending audit message.\n"));
649 rc = -1;
650 goto out;
651 }
652 rc = 0;
653 out:
654 free(msg);
655 close(audit_fd);
656 return rc;
657 }
658 #else
659 static inline
660 int send_audit_message(int success __attribute__ ((unused)),
661 security_context_t old_context
662 __attribute__ ((unused)),
663 security_context_t new_context
664 __attribute__ ((unused)), const char *ttyn
665 __attribute__ ((unused)))
666 {
667 return 0;
668 }
669 #endif
670
671 /**
672 * This function attempts to relabel the tty. If this function fails, then
673 * the fd is closed, the contexts are free'd and -1 is returned. On success,
674 * a valid fd is returned and tty_context and new_tty_context are set.
675 *
676 * This function will not fail if it can not relabel the tty when selinux is
677 * in permissive mode.
678 */
679 static int relabel_tty(const char *ttyn, security_context_t new_context,
680 security_context_t * tty_context,
681 security_context_t * new_tty_context)
682 {
683 int fd;
684 int enforcing = security_getenforce();
685 security_context_t tty_con = NULL;
686 security_context_t new_tty_con = NULL;
687
688 if (!ttyn)
689 return 0;
690
691 if (enforcing < 0) {
692 fprintf(stderr, _("Could not determine enforcing mode.\n"));
693 return -1;
694 }
695
696 /* Re-open TTY descriptor */
697 fd = open(ttyn, O_RDWR | O_NONBLOCK);
698 if (fd < 0) {
699 fprintf(stderr, _("Error! Could not open %s.\n"), ttyn);
700 return fd;
701 }
702 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
703
704 if (fgetfilecon(fd, &tty_con) < 0) {
705 fprintf(stderr, _("%s! Could not get current context "
706 "for %s, not relabeling tty.\n"),
707 enforcing ? "Error" : "Warning", ttyn);
708 if (enforcing)
709 goto close_fd;
710 }
711
712 if (tty_con &&
713 (security_compute_relabel(new_context, tty_con,
714 SECCLASS_CHR_FILE, &new_tty_con) < 0)) {
715 fprintf(stderr, _("%s! Could not get new context for %s, "
716 "not relabeling tty.\n"),
717 enforcing ? "Error" : "Warning", ttyn);
718 if (enforcing)
719 goto close_fd;
720 }
721
722 if (new_tty_con)
723 if (fsetfilecon(fd, new_tty_con) < 0) {
724 fprintf(stderr,
725 _("%s! Could not set new context for %s\n"),
726 enforcing ? "Error" : "Warning", ttyn);
727 freecon(new_tty_con);
728 new_tty_con = NULL;
729 if (enforcing)
730 goto close_fd;
731 }
732
733 *tty_context = tty_con;
734 *new_tty_context = new_tty_con;
735 return fd;
736
737 close_fd:
738 freecon(tty_con);
739 close(fd);
740 return -1;
741 }
742
743 /**
744 * This function attempts to revert the relabeling done to the tty.
745 * fd - referencing the opened ttyn
746 * ttyn - name of tty to restore
747 * tty_context - original context of the tty
748 * new_tty_context - context tty was relabeled to
749 *
750 * Returns zero on success, non-zero otherwise
751 */
752 static int restore_tty_label(int fd, const char *ttyn,
753 security_context_t tty_context,
754 security_context_t new_tty_context)
755 {
756 int rc = 0;
757 security_context_t chk_tty_context = NULL;
758
759 if (!ttyn)
760 goto skip_relabel;
761
762 if (!new_tty_context)
763 goto skip_relabel;
764
765 /* Verify that the tty still has the context set by newrole. */
766 if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
767 fprintf(stderr, "Could not fgetfilecon %s.\n", ttyn);
768 goto skip_relabel;
769 }
770
771 if ((rc = strcmp(chk_tty_context, new_tty_context))) {
772 fprintf(stderr, _("%s changed labels.\n"), ttyn);
773 goto skip_relabel;
774 }
775
776 if ((rc = fsetfilecon(fd, tty_context)) < 0)
777 fprintf(stderr,
778 _("Warning! Could not restore context for %s\n"), ttyn);
779 skip_relabel:
780 freecon(chk_tty_context);
781 return rc;
782 }
783
784 /**
785 * Parses and validates the provided command line options and
786 * constructs a new context based on our old context and the
787 * arguments specified on the command line. On success
788 * new_context will be set to valid values, otherwise its value
789 * is left unchanged.
790 *
791 * Returns zero on success, non-zero otherwise.
792 */
793 static int parse_command_line_arguments(int argc, char **argv, char *ttyn,
794 security_context_t old_context,
795 security_context_t * new_context,
796 int *preserve_environment)
797 {
798 int flag_index; /* flag index in argv[] */
799 int clflag; /* holds codes for command line flags */
800 char *role_s = NULL; /* role spec'd by user in argv[] */
801 char *type_s = NULL; /* type spec'd by user in argv[] */
802 char *type_ptr = NULL; /* stores malloc'd data from get_default_type */
803 char *level_s = NULL; /* level spec'd by user in argv[] */
804 char *range_ptr = NULL;
805 security_context_t new_con = NULL;
806 security_context_t tty_con = NULL;
807 context_t context = NULL; /* manipulatable form of new_context */
808 const struct option long_options[] = {
809 {"role", 1, 0, 'r'},
810 {"type", 1, 0, 't'},
811 {"level", 1, 0, 'l'},
812 {"preserve-environment", 0, 0, 'p'},
813 {"version", 0, 0, 'V'},
814 {NULL, 0, 0, 0}
815 };
816
817 *preserve_environment = 0;
818 while (1) {
819 clflag = getopt_long(argc, argv, "r:t:l:pV", long_options,
820 &flag_index);
821 if (clflag == -1)
822 break;
823
824 switch (clflag) {
825 case 'V':
826 printf("newrole: %s version %s\n", PACKAGE, VERSION);
827 exit(0);
828 break;
829 case 'p':
830 *preserve_environment = 1;
831 break;
832 case 'r':
833 if (role_s) {
834 fprintf(stderr,
835 _("Error: multiple roles specified\n"));
836 return -1;
837 }
838 role_s = optarg;
839 break;
840 case 't':
841 if (type_s) {
842 fprintf(stderr,
843 _("Error: multiple types specified\n"));
844 return -1;
845 }
846 type_s = optarg;
847 break;
848 case 'l':
849 if (!is_selinux_mls_enabled()) {
850 fprintf(stderr, _("Sorry, -l may be used with "
851 "SELinux MLS support.\n"));
852 return -1;
853 }
854 if (level_s) {
855 fprintf(stderr, _("Error: multiple levels "
856 "specified\n"));
857 return -1;
858 }
859 if (ttyn) {
860 if (fgetfilecon(STDIN_FILENO, &tty_con) >= 0) {
861 if (selinux_check_securetty_context
862 (tty_con) < 0) {
863 fprintf(stderr,
864 _
865 ("Error: you are not allowed to change levels on a non secure terminal \n"));
866 freecon(tty_con);
867 return -1;
868 }
869 freecon(tty_con);
870 }
871 }
872
873 level_s = optarg;
874 break;
875 default:
876 fprintf(stderr, "%s\n", USAGE_STRING);
877 return -1;
878 }
879 }
880
881 /* Verify that the combination of command-line arguments are viable */
882 if (!(role_s || type_s || level_s)) {
883 fprintf(stderr, "%s\n", USAGE_STRING);
884 return -1;
885 }
886
887 /* Fill in a default type if one hasn't been specified. */
888 if (role_s && !type_s) {
889 /* get_default_type() returns malloc'd memory */
890 if (get_default_type(role_s, &type_ptr)) {
891 fprintf(stderr, _("Couldn't get default type.\n"));
892 send_audit_message(0, old_context, new_con, ttyn);
893 return -1;
894 }
895 type_s = type_ptr;
896 }
897
898 /* Create a temporary new context structure we extract and modify */
899 context = context_new(old_context);
900 if (!context) {
901 fprintf(stderr, _("failed to get new context.\n"));
902 goto err_free;
903 }
904
905 /* Modify the temporary new context */
906 if (role_s)
907 if (context_role_set(context, role_s)) {
908 fprintf(stderr, _("failed to set new role %s\n"),
909 role_s);
910 goto err_free;
911 }
912
913 if (type_s)
914 if (context_type_set(context, type_s)) {
915 fprintf(stderr, _("failed to set new type %s\n"),
916 type_s);
917 goto err_free;
918 }
919
920 if (level_s) {
921 range_ptr =
922 build_new_range(level_s, context_range_get(context));
923 if (!range_ptr) {
924 fprintf(stderr,
925 _("failed to build new range with level %s\n"),
926 level_s);
927 goto err_free;
928 }
929 if (context_range_set(context, range_ptr)) {
930 fprintf(stderr, _("failed to set new range %s\n"),
931 range_ptr);
932 goto err_free;
933 }
934 }
935
936 /* Construct the final new context */
937 if (!(new_con = context_str(context))) {
938 fprintf(stderr, _("failed to convert new context to string\n"));
939 goto err_free;
940 }
941
942 if (security_check_context(new_con) < 0) {
943 fprintf(stderr, _("%s is not a valid context\n"), new_con);
944 send_audit_message(0, old_context, new_con, ttyn);
945 goto err_free;
946 }
947
948 *new_context = strdup(new_con);
949 if (!*new_context) {
950 fprintf(stderr, _("Unable to allocate memory for new_context"));
951 goto err_free;
952 }
953
954 free(type_ptr);
955 free(range_ptr);
956 context_free(context);
957 return 0;
958
959 err_free:
960 free(type_ptr);
961 free(range_ptr);
962 /* Don't free new_con, context_free(context) handles this */
963 context_free(context);
964 return -1;
965 }
966
967 /**
968 * Take care of any signal setup
969 */
970 static int set_signal_handles()
971 {
972 sigset_t empty;
973
974 /* Empty the signal mask in case someone is blocking a signal */
975 if (sigemptyset(&empty)) {
976 fprintf(stderr, _("Unable to obtain empty signal set\n"));
977 return -1;
978 }
979
980 (void)sigprocmask(SIG_SETMASK, &empty, NULL);
981
982 /* Terminate on SIGHUP. */
983 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
984 fprintf(stderr, _("Unable to set SIGHUP handler\n"));
985 return -1;
986 }
987
988 return 0;
989 }
990
991 /************************************************************************
992 *
993 * All code used for both PAM and shadow passwd goes in this section.
994 *
995 ************************************************************************/
996
997 int main(int argc, char *argv[])
998 {
999 security_context_t new_context = NULL; /* target security context */
1000 security_context_t old_context = NULL; /* original securiy context */
1001 security_context_t tty_context = NULL; /* current context of tty */
1002 security_context_t new_tty_context = NULL; /* new context of tty */
1003
1004 struct passwd pw; /* struct derived from passwd file line */
1005 char *ttyn = NULL; /* tty path */
1006
1007 char **old_environ;
1008 int preserve_environment;
1009
1010 int fd;
1011 pid_t childPid = 0;
1012 char *shell_argv0 = NULL;
1013
1014 #ifdef USE_PAM
1015 int rc;
1016 int pam_status; /* pam return code */
1017 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
1018
1019 /* This is a jump table of functions for PAM to use when it wants to *
1020 * communicate with the user. We'll be using misc_conv(), which is *
1021 * provided for us via pam_misc.h. */
1022 struct pam_conv pam_conversation = {
1023 misc_conv,
1024 NULL
1025 };
1026 #endif
1027
1028 /*
1029 * Step 0: Setup
1030 *
1031 * Do some intial setup, including dropping capabilities, checking
1032 * if it makes sense to continue to run newrole, and setting up
1033 * a scrubbed environment.
1034 */
1035 if (drop_capabilities(FALSE)) {
1036 perror(_("Sorry, newrole failed to drop capabilities\n"));
1037 return -1;
1038 }
1039 if (set_signal_handles())
1040 return -1;
1041
1042 #ifdef USE_NLS
1043 setlocale(LC_ALL, "");
1044 bindtextdomain(PACKAGE, LOCALEDIR);
1045 textdomain(PACKAGE);
1046 #endif
1047
1048 old_environ = environ;
1049 environ = NULL;
1050
1051 if (!is_selinux_enabled()) {
1052 fprintf(stderr, _("Sorry, newrole may be used only on "
1053 "a SELinux kernel.\n"));
1054 return -1;
1055 }
1056
1057 if (security_getenforce() < 0) {
1058 fprintf(stderr, _("Could not determine enforcing mode.\n"));
1059 return -1;
1060 }
1061
1062 /*
1063 * Step 1: Parse command line and valid arguments
1064 *
1065 * old_context and ttyn are required for audit logging,
1066 * context validation and pam
1067 */
1068 if (getprevcon(&old_context)) {
1069 fprintf(stderr, _("failed to get old_context.\n"));
1070 return -1;
1071 }
1072
1073 ttyn = ttyname(STDIN_FILENO);
1074 if (!ttyn || *ttyn == '\0') {
1075 fprintf(stderr,
1076 _("Warning! Could not retrieve tty information.\n"));
1077 }
1078
1079 if (parse_command_line_arguments(argc, argv, ttyn, old_context,
1080 &new_context, &preserve_environment))
1081 return -1;
1082
1083 /*
1084 * Step 2: Authenticate the user.
1085 *
1086 * Re-authenticate the user running this program.
1087 * This is just to help confirm user intent (vs. invocation by
1088 * malicious software), not to authorize the operation (which is covered
1089 * by policy). Trusted path mechanism would be preferred.
1090 */
1091 if (extract_pw_data(&pw))
1092 goto err_free;
1093
1094 #ifdef USE_PAM
1095 if (read_pam_config()) {
1096 fprintf(stderr,
1097 _("error on reading PAM service configuration.\n"));
1098 goto err_free;
1099 }
1100
1101 if (app_service_names != NULL && optind < argc) {
1102 if (strcmp(argv[optind], "-c") == 0 && optind < (argc - 1)) {
1103 /*
1104 * Check for a separate pam service name for the
1105 * command when invoked by newrole.
1106 */
1107 char *cmd = NULL;
1108 rc = sscanf(argv[optind + 1], "%as", &cmd);
1109 if (rc != EOF && cmd) {
1110 char *app_service_name =
1111 (char *)hashtab_search(app_service_names,
1112 cmd);
1113 free(cmd);
1114 if (app_service_name != NULL)
1115 service_name = app_service_name;
1116 }
1117 }
1118 }
1119
1120 pam_status = pam_start(service_name, pw.pw_name, &pam_conversation,
1121 &pam_handle);
1122 if (pam_status != PAM_SUCCESS) {
1123 fprintf(stderr, _("failed to initialize PAM\n"));
1124 goto err_free;
1125 }
1126
1127 if (!authenticate_via_pam(ttyn, pam_handle))
1128 #else
1129 if (!authenticate_via_shadow_passwd(pw.pw_name))
1130 #endif
1131 {
1132 fprintf(stderr, _("newrole: incorrect password for %s\n"),
1133 pw.pw_name);
1134 send_audit_message(0, old_context, new_context, ttyn);
1135 goto err_close_pam;
1136 }
1137
1138 /*
1139 * Step 3: Handle relabeling of the tty.
1140 *
1141 * Once we authenticate the user, we know that we want to proceed with
1142 * the action. Prior to this point, no changes are made the to system.
1143 */
1144 fd = relabel_tty(ttyn, new_context, &tty_context, &new_tty_context);
1145 if (fd < 0)
1146 goto err_close_pam;
1147
1148 /*
1149 * Step 4: Fork
1150 *
1151 * Fork, allowing parent to clean up after shell has executed.
1152 * Child: reopen stdin, stdout, stderr and exec shell
1153 * Parnet: wait for child to die and restore tty's context
1154 */
1155 childPid = fork();
1156 if (childPid < 0) {
1157 /* fork failed, no child to worry about */
1158 int errsv = errno;
1159 fprintf(stderr, _("newrole: failure forking: %s"),
1160 strerror(errsv));
1161 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context))
1162 fprintf(stderr, _("Unable to restore tty label...\n"));
1163 if (close(fd))
1164 fprintf(stderr, _("Failed to close tty properly\n"));
1165 goto err_close_pam;
1166 } else if (childPid) {
1167 /* PARENT
1168 * It doesn't make senes to exit early on errors at this point,
1169 * since we are doing cleanup which needs to be done.
1170 * We can exit with a bad rc though
1171 */
1172 pid_t pid;
1173 int exit_code = 0;
1174 int status;
1175
1176 do {
1177 pid = wait(&status);
1178 } while (pid < 0 && errno == EINTR);
1179
1180 /* Preserve child exit status, unless there is another error. */
1181 if (WIFEXITED(status))
1182 exit_code = WEXITSTATUS(status);
1183
1184 if (restore_tty_label(fd, ttyn, tty_context, new_tty_context)) {
1185 fprintf(stderr, _("Unable to restore tty label...\n"));
1186 exit_code = -1;
1187 }
1188 freecon(tty_context);
1189 freecon(new_tty_context);
1190 if (close(fd)) {
1191 fprintf(stderr, _("Failed to close tty properly\n"));
1192 exit_code = -1;
1193 }
1194 #ifdef USE_PAM
1195 #ifdef NAMESPACE_PRIV
1196 pam_status = pam_close_session(pam_handle, 0);
1197 if (pam_status != PAM_SUCCESS) {
1198 fprintf(stderr, "pam_close_session failed with %s\n",
1199 pam_strerror(pam_handle, pam_status));
1200 exit_code = -1;
1201 }
1202 #endif
1203 rc = pam_end(pam_handle, pam_status);
1204 if (rc != PAM_SUCCESS) {
1205 fprintf(stderr, "pam_end failed with %s\n",
1206 pam_strerror(pam_handle, rc));
1207 exit_code = -1;
1208 }
1209 hashtab_map(app_service_names, free_hashtab_entry, NULL);
1210 hashtab_destroy(app_service_names);
1211 #endif
1212 free(pw.pw_name);
1213 free(pw.pw_dir);
1214 free(pw.pw_shell);
1215 free(shell_argv0);
1216 return exit_code;
1217 }
1218
1219 /* CHILD */
1220 /* Close the tty and reopen descriptors 0 through 2 */
1221 if (ttyn) {
1222 if (close(fd) || close(0) || close(1) || close(2)) {
1223 fprintf(stderr, _("Could not close descriptors.\n"));
1224 goto err_close_pam;
1225 }
1226 fd = open(ttyn, O_RDONLY | O_NONBLOCK);
1227 if (fd != 0)
1228 goto err_close_pam;
1229 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1230 fd = open(ttyn, O_RDWR | O_NONBLOCK);
1231 if (fd != 1)
1232 goto err_close_pam;
1233 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1234 fd = open(ttyn, O_RDWR | O_NONBLOCK);
1235 if (fd != 2)
1236 goto err_close_pam;
1237 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1238
1239 }
1240 /*
1241 * Step 5: Execute a new shell with the new context in `new_context'.
1242 *
1243 * Establish context, namesapce and any options for the new shell
1244 */
1245 if (optind < 1)
1246 optind = 1;
1247
1248 /* This is ugly, but use newrole's argv for the exec'd shells argv */
1249 if (asprintf(&shell_argv0, "-%s", pw.pw_shell) < 0) {
1250 fprintf(stderr, _("Error allocating shell's argv0.\n"));
1251 shell_argv0 = NULL;
1252 goto err_close_pam;
1253 }
1254 argv[optind - 1] = shell_argv0;
1255
1256 if (setexeccon(new_context)) {
1257 fprintf(stderr, _("Could not set exec context to %s.\n"),
1258 new_context);
1259 goto err_close_pam;
1260 }
1261 #ifdef NAMESPACE_PRIV
1262 /* Ask PAM to setup session for user running this program */
1263 pam_status = pam_open_session(pam_handle, 0);
1264 if (pam_status != PAM_SUCCESS) {
1265 fprintf(stderr, "pam_open_session failed with %s\n",
1266 pam_strerror(pam_handle, pam_status));
1267 goto err_close_pam;
1268 }
1269 #endif
1270
1271 if (send_audit_message(1, old_context, new_context, ttyn))
1272 goto err_close_pam_session;
1273 freecon(old_context); old_context=NULL;
1274 freecon(new_context); new_context=NULL;
1275
1276 #ifdef NAMESPACE_PRIV
1277 if (transition_to_caller_uid())
1278 goto err_close_pam_session;
1279 #endif
1280
1281 if (drop_capabilities(TRUE))
1282 goto err_close_pam_session;
1283
1284 /* Handle environment changes */
1285 if (restore_environment(preserve_environment, old_environ, &pw)) {
1286 fprintf(stderr, _("Unable to restore the environment, "
1287 "aborting\n"));
1288 goto err_close_pam_session;
1289 }
1290 execv(pw.pw_shell, argv + optind - 1);
1291
1292 /*
1293 * Error path cleanup
1294 *
1295 * If we reach here, then we failed to exec the new shell.
1296 */
1297 perror(_("failed to exec shell\n"));
1298 err_close_pam_session:
1299 #ifdef NAMESPACE_PRIV
1300 pam_status = pam_close_session(pam_handle, 0);
1301 if (pam_status != PAM_SUCCESS)
1302 fprintf(stderr, "pam_close_session failed with %s\n",
1303 pam_strerror(pam_handle, pam_status));
1304 #endif
1305 err_close_pam:
1306 #ifdef USE_PAM
1307 rc = pam_end(pam_handle, pam_status);
1308 if (rc != PAM_SUCCESS)
1309 fprintf(stderr, "pam_end failed with %s\n",
1310 pam_strerror(pam_handle, rc));
1311 #endif
1312 err_free:
1313 freecon(tty_context);
1314 freecon(new_tty_context);
1315 freecon(old_context);
1316 freecon(new_context);
1317 free(pw.pw_name);
1318 free(pw.pw_dir);
1319 free(pw.pw_shell);
1320 free(shell_argv0);
1321 #ifdef USE_PAM
1322 if (app_service_names) {
1323 hashtab_map(app_service_names, free_hashtab_entry, NULL);
1324 hashtab_destroy(app_service_names);
1325 }
1326 #endif
1327 return -1;
1328 } /* main() */
Location | Tool | Test ID | Issue | |
---|---|---|---|---|
restorecond.c:106:2 | gcc | unused-result | write_pid_file | ignoring return value of 'write', declared with attribute warn_unused_result |
restorecond.c:211:12 | clang-analyzer | Access to field 'pw_dir' results in a dereference of a null pointer (loaded from variable 'pwd') | ||
restorecond.c:224:9 | gcc | unused-result | main | ignoring return value of 'daemon', declared with attribute warn_unused_result |
1 /*
2 * restorecond
3 *
4 * Copyright (C) 2006-2009 Red Hat
5 * see file 'COPYING' for use and warranty information
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 .*
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 * 02111-1307 USA
21 *
22 * Authors:
23 * Dan Walsh <dwalsh@redhat.com>
24 *
25 */
26
27 /*
28 * PURPOSE:
29 * This daemon program watches for the creation of files listed in a config file
30 * and makes sure that there security context matches the systems defaults
31 *
32 * USAGE:
33 * restorecond [-d] [-u] [-v] [-f restorecond_file ]
34 *
35 * -d Run in debug mode
36 * -f Use alternative restorecond_file
37 * -u Run in user mode
38 * -v Run in verbose mode (Report missing files)
39 *
40 * EXAMPLE USAGE:
41 * restorecond
42 *
43 */
44
45 #define _GNU_SOURCE
46 #include <sys/inotify.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include "../setfiles/restore.h"
54 #include <sys/types.h>
55 #include <syslog.h>
56 #include <limits.h>
57 #include <pwd.h>
58 #include <sys/stat.h>
59 #include <string.h>
60 #include <stdio.h>
61 #include <fcntl.h>
62 #include "restorecond.h"
63 #include "utmpwatcher.h"
64
65 const char *homedir;
66 static int master_fd = -1;
67
68 static char *server_watch_file = "/etc/selinux/restorecond.conf";
69 static char *user_watch_file = "/etc/selinux/restorecond_user.conf";
70 static char *watch_file;
71 static struct restore_opts r_opts;
72
73 #include <selinux/selinux.h>
74
75 int debug_mode = 0;
76 int terminate = 0;
77 int master_wd = -1;
78 int run_as_user = 0;
79
80 static void done(void) {
81 watch_list_free(master_fd);
82 close(master_fd);
83 utmpwatcher_free();
84 matchpathcon_fini();
85 }
86
87 static const char *pidfile = "/var/run/restorecond.pid";
88
89 static int write_pid_file(void)
90 {
91 int pidfd, len;
92 char val[16];
93
94 len = snprintf(val, sizeof(val), "%u\n", getpid());
95 if (len < 0) {
96 syslog(LOG_ERR, "Pid error (%s)", strerror(errno));
97 pidfile = 0;
98 return 1;
99 }
100 pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
101 if (pidfd < 0) {
102 syslog(LOG_ERR, "Unable to set pidfile (%s)", strerror(errno));
103 pidfile = 0;
104 return 1;
105 }
106 (void)write(pidfd, val, (unsigned int)len);
(emitted by gcc) 107 close(pidfd);
108 return 0;
109 }
110
111 /*
112 * SIGTERM handler
113 */
114 static void term_handler()
115 {
116 terminate = 1;
117 /* trigger a failure in the watch */
118 close(master_fd);
119 }
120
121 static void usage(char *program)
122 {
123 printf("%s [-d] [-f restorecond_file ] [-u] [-v] \n", program);
124 }
125
126 void exitApp(const char *msg)
127 {
128 perror(msg);
129 exit(-1);
130 }
131
132 /*
133 Add a file to the watch list. We are watching for file creation, so we actually
134 put the watch on the directory and then examine all files created in that directory
135 to see if it is one that we are watching.
136 */
137
138 int main(int argc, char **argv)
139 {
140 int opt;
141 struct sigaction sa;
142
143 memset(&r_opts, 0, sizeof(r_opts));
144
145 r_opts.progress = 0;
146 r_opts.count = 0;
147 r_opts.debug = 0;
148 r_opts.change = 1;
149 r_opts.verbose = 0;
150 r_opts.logging = 0;
151 r_opts.rootpath = NULL;
152 r_opts.rootpathlen = 0;
153 r_opts.outfile = NULL;
154 r_opts.force = 0;
155 r_opts.hard_links = 0;
156 r_opts.abort_on_error = 0;
157 r_opts.add_assoc = 0;
158 r_opts.expand_realpath = 0;
159 r_opts.fts_flags = FTS_PHYSICAL;
160 r_opts.selabel_opt_validate = NULL;
161 r_opts.selabel_opt_path = NULL;
162 r_opts.ignore_enoent = 1;
163
164 restore_init(&r_opts);
165 /* If we are not running SELinux then just exit */
166 if (is_selinux_enabled() != 1) return 0;
167
168 /* Register sighandlers */
169 sa.sa_flags = 0;
170 sa.sa_handler = term_handler;
171 sigemptyset(&sa.sa_mask);
172 sigaction(SIGTERM, &sa, NULL);
173
174 set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
175
176 exclude_non_seclabel_mounts();
177 atexit( done );
178 while ((opt = getopt(argc, argv, "hdf:uv")) > 0) {
179 switch (opt) {
180 case 'd':
181 debug_mode = 1;
182 break;
183 case 'f':
184 watch_file = optarg;
185 break;
186 case 'u':
187 run_as_user = 1;
188 break;
189 case 'h':
190 usage(argv[0]);
191 exit(0);
192 break;
193 case 'v':
194 r_opts.verbose++;
195 break;
196 case '?':
197 usage(argv[0]);
198 exit(-1);
199 }
200 }
201
202 master_fd = inotify_init();
203 if (master_fd < 0)
204 exitApp("inotify_init");
205
206 uid_t uid = getuid();
207 struct passwd *pwd = getpwuid(uid);
208 if (!pwd)
209 exitApp("getpwuid");
210
211 homedir = pwd->pw_dir;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
212 if (uid != 0) {
213 if (run_as_user)
214 return server(master_fd, user_watch_file);
215 if (start() != 0)
216 return server(master_fd, user_watch_file);
217 return 0;
218 }
219
220 watch_file = server_watch_file;
221 read_config(master_fd, watch_file);
222
223 if (!debug_mode)
224 daemon(0, 0);
(emitted by gcc) 225
226 write_pid_file();
227
228 while (watch(master_fd, watch_file) == 0) {
229 };
230
231 watch_list_free(master_fd);
232 close(master_fd);
233 matchpathcon_fini();
234 if (pidfile)
235 unlink(pidfile);
236
237 return 0;
238 }
Location | Tool | Test ID | Issue | |
---|---|---|---|---|
stringslist.c:50:2 | clang-analyzer | Access to field 'string' results in a dereference of a null pointer (loaded from variable 'newptr') |
1 /*
2 * Copyright (C) 2006, 2008 Red Hat
3 * see file 'COPYING' for use and warranty information
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 .*
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 * 02111-1307 USA
19 *
20 * Authors:
21 * Dan Walsh <dwalsh@redhat.com>
22 *
23 */
24
25 #include <string.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include "stringslist.h"
29 #include "restorecond.h"
30 #include <fnmatch.h>
31
32 /* Sorted lists */
33 void strings_list_add(struct stringsList **list, const char *string)
34 {
35 struct stringsList *ptr = *list;
36 struct stringsList *prev = NULL;
37 struct stringsList *newptr = NULL;
38 while (ptr) {
39 int cmp = strcmp(string, ptr->string);
40 if (cmp < 0)
41 break; /* Not on list break out to add */
42 if (cmp == 0)
43 return; /* Already on list */
44 prev = ptr;
45 ptr = ptr->next;
46 }
47 newptr = calloc(1, sizeof(struct stringsList));
48 if (!newptr)
49 exitApp("Out of Memory");
50 newptr->string = strdup(string);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
51 newptr->next = ptr;
52 if (prev)
53 prev->next = newptr;
54 else
55 *list = newptr;
56 }
57
58 int strings_list_find(struct stringsList *ptr, const char *string, int *exact)
59 {
60 while (ptr) {
61 *exact = strcmp(ptr->string, string) == 0;
62 int cmp = fnmatch(ptr->string, string, 0);
63 if (cmp == 0)
64 return 0; /* Match found */
65 ptr = ptr->next;
66 }
67 return -1;
68 }
69
70 void strings_list_free(struct stringsList *ptr)
71 {
72 struct stringsList *prev = NULL;
73 while (ptr) {
74 free(ptr->string);
75 prev = ptr;
76 ptr = ptr->next;
77 free(prev);
78 }
79 }
80
81 int strings_list_diff(struct stringsList *from, struct stringsList *to)
82 {
83 while (from != NULL && to != NULL) {
84 if (strcmp(from->string, to->string) != 0)
85 return 1;
86 from = from->next;
87 to = to->next;
88 }
89 if (from != NULL || to != NULL)
90 return 1;
91 return 0;
92 }
93
94 void strings_list_print(struct stringsList *ptr)
95 {
96 while (ptr) {
97 printf("%s\n", ptr->string);
98 ptr = ptr->next;
99 }
100 }
101
102 #ifdef TEST
103 void exitApp(const char *msg)
104 {
105 perror(msg);
106 exit(-1);
107 }
108
109 int main(int argc, char **argv)
110 {
111 struct stringsList *list = NULL;
112 struct stringsList *list1 = NULL;
113 strings_list_add(&list, "/etc/resolv.conf");
114 strings_list_add(&list, "/etc/walsh");
115 strings_list_add(&list, "/etc/mtab");
116 strings_list_add(&list, "/etc/walsh");
117 if (strings_list_diff(list, list) != 0)
118 printf("strings_list_diff test1 bug\n");
119 strings_list_add(&list1, "/etc/walsh");
120 if (strings_list_diff(list, list1) == 0)
121 printf("strings_list_diff test2 bug\n");
122 strings_list_add(&list1, "/etc/walsh");
123 strings_list_add(&list1, "/etc/walsh/*");
124 strings_list_add(&list1, "/etc/resolv.conf");
125 strings_list_add(&list1, "/etc/mtab1");
126 if (strings_list_diff(list, list1) == 0)
127 printf("strings_list_diff test3 bug\n");
128 printf("strings list\n");
129 strings_list_print(list);
130 printf("strings list1\n");
131 strings_list_find(list1, "/etc/walsh/dan");
132 strings_list_print(list1);
133 strings_list_free(list);
134 strings_list_free(list1);
135 }
136 #endif
No issues found
1 /*
2 * restorecond
3 *
4 * Copyright (C) 2006-2009 Red Hat
5 * see file 'COPYING' for use and warranty information
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 .*
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 * 02111-1307 USA
21 *
22 * Authors:
23 * Dan Walsh <dwalsh@redhat.com>
24 *
25 */
26
27 #define _GNU_SOURCE
28 #include <sys/inotify.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <syslog.h>
39 #include <limits.h>
40 #include <fcntl.h>
41
42 #include "restorecond.h"
43 #include "stringslist.h"
44 #include <glib.h>
45 #ifdef HAVE_DBUS
46 #include <dbus/dbus.h>
47 #include <dbus/dbus-glib.h>
48 #include <dbus/dbus-glib-lowlevel.h>
49
50 static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data);
51
52 static const char *PATH="/org/selinux/Restorecond";
53 //static const char *BUSNAME="org.selinux.Restorecond";
54 static const char *INTERFACE="org.selinux.RestorecondIface";
55 static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'";
56
57
58 static DBusHandlerResult
59 signal_filter (DBusConnection *connection __attribute__ ((__unused__)), DBusMessage *message, void *user_data)
60 {
61 /* User data is the event loop we are running in */
62 GMainLoop *loop = user_data;
63
64 /* A signal from the bus saying we are about to be disconnected */
65 if (dbus_message_is_signal
66 (message, INTERFACE, "Stop")) {
67
68 /* Tell the main loop to quit */
69 g_main_loop_quit (loop);
70 /* We have handled this message, don't pass it on */
71 return DBUS_HANDLER_RESULT_HANDLED;
72 }
73 /* A Ping signal on the com.burtonini.dbus.Signal interface */
74 else if (dbus_message_is_signal (message, INTERFACE, "Start")) {
75 DBusError error;
76 dbus_error_init (&error);
77 g_print("Start received\n");
78 return DBUS_HANDLER_RESULT_HANDLED;
79 }
80 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
81 }
82
83 static int dbus_server(GMainLoop *loop) {
84 DBusConnection *bus;
85 DBusError error;
86 dbus_error_init (&error);
87 bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
88 if (bus) {
89 dbus_connection_setup_with_g_main (bus, NULL);
90
91 /* listening to messages from all objects as no path is specified */
92 dbus_bus_add_match (bus, RULE, &error); // see signals from the given interfacey
93 dbus_connection_add_filter (bus, signal_filter, loop, NULL);
94 return 0;
95 }
96 return -1;
97 }
98
99 #endif
100 #include <selinux/selinux.h>
101 #include <sys/file.h>
102
103 /* size of the event structure, not counting name */
104 #define EVENT_SIZE (sizeof (struct inotify_event))
105 /* reasonable guess as to size of 1024 events */
106 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
107
108 static gboolean
109 io_channel_callback
110 (GIOChannel *source,
111 GIOCondition condition,
112 gpointer data __attribute__((__unused__)))
113 {
114
115 char buffer[BUF_LEN+1];
116 gsize bytes_read;
117 unsigned int i = 0;
118
119 if (condition & G_IO_IN) {
120 /* Data is available. */
121 g_io_channel_read_chars
122 (source, buffer,
123 sizeof (buffer),
124 &bytes_read, NULL);
125
126 if (! bytes_read) {
127 /* Sesssion/Terminal Ended */
128 exit(0);
129 }
130
131 while (i < bytes_read) {
132 struct inotify_event *event;
133 event = (struct inotify_event *)&buffer[i];
134 if (debug_mode)
135 printf("wd=%d mask=%u cookie=%u len=%u\n",
136 event->wd, event->mask,
137 event->cookie, event->len);
138 if (event->len)
139 watch_list_find(event->wd, event->name);
140
141 i += EVENT_SIZE + event->len;
142 }
143 }
144
145 /* An error happened while reading
146 the file. */
147
148 if (condition & G_IO_NVAL)
149 return FALSE;
150
151 /* We have reached the end of the
152 file. */
153
154 if (condition & G_IO_HUP) {
155 g_io_channel_shutdown (source, 0, NULL);
156 exit(0);
157 return FALSE;
158 }
159
160 /* Returning TRUE will make sure
161 the callback remains associated
162 to the channel. */
163
164 return TRUE;
165 }
166
167 int start() {
168 #ifdef HAVE_DBUS
169 DBusConnection *bus;
170 DBusError error;
171 DBusMessage *message;
172
173 /* Get a connection to the session bus */
174 dbus_error_init (&error);
175 bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
176 if (!bus) {
177 if (debug_mode)
178 g_warning ("Failed to connect to the D-BUS daemon: %s", error.message);
179 dbus_error_free (&error);
180 return 1;
181 }
182
183
184 /* Create a new signal "Start" on the interface,
185 * from the object */
186 message = dbus_message_new_signal (PATH,
187 INTERFACE, "Start");
188 /* Send the signal */
189 dbus_connection_send (bus, message, NULL);
190 /* Free the signal now we have finished with it */
191 dbus_message_unref (message);
192 #endif /* HAVE_DBUS */
193 return 0;
194 }
195
196 static int local_server() {
197 // ! dbus, run as local service
198 char *ptr=NULL;
199 if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) {
200 if (debug_mode)
201 perror("asprintf");
202 return -1;
203 }
204 int fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, S_IRUSR | S_IWUSR);
205 if (debug_mode)
206 g_warning ("Lock file: %s", ptr);
207
208 free(ptr);
209 if (fd < 0) {
210 if (debug_mode)
211 perror("open");
212 return -1;
213 }
214 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
215 if (debug_mode)
216 perror("flock");
217 return -1;
218 }
219 /* watch for stdin/terminal going away */
220 GIOChannel *in = g_io_channel_unix_new(0);
221 g_io_add_watch_full( in,
222 G_PRIORITY_HIGH,
223 G_IO_IN|G_IO_ERR|G_IO_HUP,
224 io_channel_callback, NULL, NULL);
225
226 return 0;
227 }
228
229 int server(int master_fd, const char *watch_file) {
230 GMainLoop *loop;
231
232 loop = g_main_loop_new (NULL, FALSE);
233
234 #ifdef HAVE_DBUS
235 if (dbus_server(loop) != 0)
236 #endif /* HAVE_DBUS */
237 if (local_server())
238 goto end;
239
240 read_config(master_fd, watch_file);
241
242 if (watch_list_isempty()) goto end;
243
244 set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
245
246 GIOChannel *c = g_io_channel_unix_new(master_fd);
247
248 g_io_add_watch_full( c,
249 G_PRIORITY_HIGH,
250 G_IO_IN|G_IO_ERR|G_IO_HUP,
251 io_channel_callback, NULL, NULL);
252
253 g_main_loop_run (loop);
254
255 end:
256 g_main_loop_unref (loop);
257 return 0;
258 }
No issues found
1 /*
2 * utmpwatcher.c
3 *
4 * Copyright (C) 2006 Red Hat
5 * see file 'COPYING' for use and warranty information
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 .*
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 * 02111-1307 USA
21 *
22 * Authors:
23 * Dan Walsh <dwalsh@redhat.com>
24 *
25 *
26 */
27
28 #define _GNU_SOURCE
29 #include <sys/inotify.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <syslog.h>
36
37 #include <limits.h>
38 #include <utmp.h>
39 #include <sys/types.h>
40 #include <pwd.h>
41 #include "restorecond.h"
42 #include "utmpwatcher.h"
43 #include "stringslist.h"
44
45 static struct stringsList *utmp_ptr = NULL;
46 static int utmp_wd = -1;
47
48 unsigned int utmpwatcher_handle(int inotify_fd, int wd)
49 {
50 int changed = 0;
51 struct utmp u;
52 char *utmp_path = "/var/run/utmp";
53 struct stringsList *prev_utmp_ptr = utmp_ptr;
54 if (wd != utmp_wd)
55 return -1;
56
57 utmp_ptr = NULL;
58 FILE *cfg = fopen(utmp_path, "r");
59 if (!cfg)
60 exitApp("Error reading utmp file.");
61
62 while (fread(&u, sizeof(struct utmp), 1, cfg) > 0) {
63 if (u.ut_type == USER_PROCESS)
64 strings_list_add(&utmp_ptr, u.ut_user);
65 }
66 fclose(cfg);
67 if (utmp_wd >= 0)
68 inotify_rm_watch(inotify_fd, utmp_wd);
69
70 utmp_wd =
71 inotify_add_watch(inotify_fd, utmp_path, IN_MOVED_FROM | IN_MODIFY);
72 if (utmp_wd == -1)
73 exitApp("Error watching utmp file.");
74
75 changed = strings_list_diff(prev_utmp_ptr, utmp_ptr);
76 if (prev_utmp_ptr) {
77 strings_list_free(prev_utmp_ptr);
78 }
79 return changed;
80 }
81
82 static void watch_file(int inotify_fd, const char *file)
83 {
84 struct stringsList *ptr = utmp_ptr;
85
86 while (ptr) {
87 struct passwd *pwd = getpwnam(ptr->string);
88 if (pwd) {
89 char *path = NULL;
90 if (asprintf(&path, "%s%s", pwd->pw_dir, file) < 0)
91 exitApp("Error allocating memory.");
92 watch_list_add(inotify_fd, path);
93 free(path);
94 }
95 ptr = ptr->next;
96 }
97 }
98
99 void utmpwatcher_add(int inotify_fd, const char *path)
100 {
101 if (utmp_ptr == NULL) {
102 utmpwatcher_handle(inotify_fd, utmp_wd);
103 }
104 watch_file(inotify_fd, path);
105 }
106
107 void utmpwatcher_free(void)
108 {
109 if (utmp_ptr)
110 strings_list_free(utmp_ptr);
111 }
112
113 #ifdef TEST
114 int main(int argc, char **argv)
115 {
116 read_utmp();
117 return 0;
118 }
119 #endif
Location | Tool | Test ID | Issue | |
---|---|---|---|---|
watch.c:82:2 | clang-analyzer | Access to field 'wd' results in a dereference of a null pointer (loaded from variable 'ptr') |
1 #define _GNU_SOURCE
2 #include <sys/inotify.h>
3 #include <errno.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <ctype.h>
9 #include <sys/types.h>
10 #include <syslog.h>
11 #include "../setfiles/restore.h"
12 #include <glob.h>
13 #include <libgen.h>
14 #include <sys/stat.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <selinux/selinux.h>
19 #include "restorecond.h"
20 #include "stringslist.h"
21 #include "utmpwatcher.h"
22
23 /* size of the event structure, not counting name */
24 #define EVENT_SIZE (sizeof (struct inotify_event))
25 /* reasonable guess as to size of 1024 events */
26 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
27
28
29 struct watchList {
30 struct watchList *next;
31 int wd;
32 char *dir;
33 struct stringsList *files;
34 };
35 struct watchList *firstDir = NULL;
36
37 int watch_list_isempty() {
38 return firstDir == NULL;
39 }
40
41 void watch_list_add(int fd, const char *path)
42 {
43 struct watchList *ptr = NULL;
44 size_t i = 0;
45 struct watchList *prev = NULL;
46 glob_t globbuf;
47 char *x = strdup(path);
48 if (!x) exitApp("Out of Memory");
49 char *file = basename(x);
50 char *dir = dirname(x);
51 ptr = firstDir;
52
53 if (exclude(path)) goto end;
54
55 globbuf.gl_offs = 1;
56 if (glob(path,
57 GLOB_TILDE | GLOB_PERIOD,
58 NULL,
59 &globbuf) >= 0) {
60 for (i=0; i < globbuf.gl_pathc; i++) {
61 int len = strlen(globbuf.gl_pathv[i]) -2;
62 if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) continue;
63 if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) continue;
64 if (process_one_realpath(globbuf.gl_pathv[i], 0) > 0)
65 process_one_realpath(globbuf.gl_pathv[i], 1);
66 }
67 globfree(&globbuf);
68 }
69
70 while (ptr != NULL) {
71 if (strcmp(dir, ptr->dir) == 0) {
72 strings_list_add(&ptr->files, file);
73 goto end;
74 }
75 prev = ptr;
76 ptr = ptr->next;
77 }
78 ptr = calloc(1, sizeof(struct watchList));
79
80 if (!ptr) exitApp("Out of Memory");
81
82 ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
83 if (ptr->wd == -1) {
84 free(ptr);
85 if (! run_as_user)
86 syslog(LOG_ERR, "Unable to watch (%s) %s\n",
87 path, strerror(errno));
88 goto end;
89 }
90
91 ptr->dir = strdup(dir);
92 if (!ptr->dir)
93 exitApp("Out of Memory");
94
95 strings_list_add(&ptr->files, file);
96 if (prev)
97 prev->next = ptr;
98 else
99 firstDir = ptr;
100
101 if (debug_mode)
102 printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file);
103
104 end:
105 free(x);
106 return;
107 }
108
109 /*
110 A file was in a direcroty has been created. This function checks to
111 see if it is one that we are watching.
112 */
113
114 int watch_list_find(int wd, const char *file)
115 {
116 struct watchList *ptr = NULL;
117 ptr = firstDir;
118 if (debug_mode)
119 printf("%d: File=%s\n", wd, file);
120 while (ptr != NULL) {
121 if (ptr->wd == wd) {
122 int exact=0;
123 if (strings_list_find(ptr->files, file, &exact) == 0) {
124 char *path = NULL;
125 if (asprintf(&path, "%s/%s", ptr->dir, file) <
126 0)
127 exitApp("Error allocating memory.");
128
129 process_one_realpath(path, 0);
130 free(path);
131 return 0;
132 }
133 if (debug_mode)
134 strings_list_print(ptr->files);
135
136 /* Not found in this directory */
137 return -1;
138 }
139 ptr = ptr->next;
140 }
141 /* Did not find a directory */
142 return -1;
143 }
144
145 void watch_list_free(int fd)
146 {
147 struct watchList *ptr = NULL;
148 struct watchList *prev = NULL;
149 ptr = firstDir;
150
151 while (ptr != NULL) {
152 inotify_rm_watch(fd, ptr->wd);
153 strings_list_free(ptr->files);
154 free(ptr->dir);
155 prev = ptr;
156 ptr = ptr->next;
157 free(prev);
158 }
159 firstDir = NULL;
160 }
161
162 /*
163 Inotify watch loop
164 */
165 int watch(int fd, const char *watch_file)
166 {
167 char buf[BUF_LEN];
168 int len, i = 0;
169 if (firstDir == NULL) return 0;
170
171 len = read(fd, buf, BUF_LEN);
172 if (len < 0) {
173 if (terminate == 0) {
174 syslog(LOG_ERR, "Read error (%s)", strerror(errno));
175 return 0;
176 }
177 syslog(LOG_ERR, "terminated");
178 return -1;
179 } else if (!len)
180 /* BUF_LEN too small? */
181 return -1;
182 while (i < len) {
183 struct inotify_event *event;
184 event = (struct inotify_event *)&buf[i];
185 if (debug_mode)
186 printf("wd=%d mask=%u cookie=%u len=%u\n",
187 event->wd, event->mask,
188 event->cookie, event->len);
189 if (event->mask & ~IN_IGNORED) {
190 if (event->wd == master_wd)
191 read_config(fd, watch_file);
192 else {
193 switch (utmpwatcher_handle(fd, event->wd)) {
194 case -1: /* Message was not for utmpwatcher */
195 if (event->len)
196 watch_list_find(event->wd, event->name);
197 break;
198 case 1: /* utmp has changed need to reload */
199 read_config(fd, watch_file);
200 break;
201
202 default: /* No users logged in or out */
203 break;
204 }
205 }
206 }
207
208 i += EVENT_SIZE + event->len;
209 }
210 return 0;
211 }
212
213 static void process_config(int fd, FILE * cfg)
214 {
215 char *line_buf = NULL;
216 size_t len = 0;
217
218 while (getline(&line_buf, &len, cfg) > 0) {
219 char *buffer = line_buf;
220 while (isspace(*buffer))
221 buffer++;
222 if (buffer[0] == '#')
223 continue;
224 int l = strlen(buffer) - 1;
225 if (l <= 0)
226 continue;
227 buffer[l] = 0;
228 if (buffer[0] == '~') {
229 if (run_as_user) {
230 char *ptr=NULL;
231 if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0)
232 exitApp("Error allocating memory.");
233
234 watch_list_add(fd, ptr);
235 free(ptr);
236 } else {
237 utmpwatcher_add(fd, &buffer[1]);
238 }
239 } else {
240 watch_list_add(fd, buffer);
241 }
242 }
243 free(line_buf);
244 }
245
246 /*
247 Read config file ignoring Comment lines
248 Files specified one per line. Files with "~" will be expanded to the logged in users
249 homedirs.
250 */
251
252 void read_config(int fd, const char *watch_file_path)
253 {
254
255 FILE *cfg = NULL;
256 if (debug_mode)
257 printf("Read Config\n");
258
259 watch_list_free(fd);
260
261 cfg = fopen(watch_file_path, "r");
262 if (!cfg){
263 perror(watch_file_path);
264 exitApp("Error reading config file");
265 }
266 process_config(fd, cfg);
267 fclose(cfg);
268
269 inotify_rm_watch(fd, master_wd);
270 master_wd =
271 inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY);
272 if (master_wd == -1)
273 exitApp("Error watching config file.");
274 }
No issues found
1 /* -*- Mode: C -*-
2 * open_init_pty.c ---
3 * Author : Manoj Srivastava ( srivasta@glaurung.internal.golden-gryphon.com )
4 * Created On : Fri Jan 14 10:48:28 2005
5 * Created On Node : glaurung.internal.golden-gryphon.com
6 * Last Modified By : Manoj Srivastava
7 * Last Modified On : Thu Sep 15 00:57:00 2005
8 * Last Machine Used: glaurung.internal.golden-gryphon.com
9 * Update Count : 92
10 * Status : Unknown, Use with caution!
11 * HISTORY :
12 * Description :
13 *
14 * Distributed under the terms of the GNU General Public License v2
15 *
16 * open_init_pty
17 *
18 * SYNOPSIS:
19 *
20 * This program allows a systems administrator to execute daemons
21 * which need to work in the initrc domain, and which need to have
22 * pty's as system_u:system_r:initrc_t
23 *
24 * USAGE:
25 *
26 * * arch-tag: a5583d39-72b9-4cdf-ba1b-5678ea4cbe20
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <errno.h>
34
35 #include <sysexits.h>
36
37 #include <pty.h> /* for openpty and forkpty */
38 #include <utmp.h> /* for login_tty */
39 #include <termios.h>
40 #include <fcntl.h>
41
42 #include <sys/select.h>
43
44 static struct termios saved_termios;
45 static int saved_fd = -1;
46 static enum { RESET, RAW, CBREAK } tty_state = RESET;
47
48 static int tty_semi_raw(int fd)
49 {
50 struct termios buf;
51
52 if (tty_state == RESET) {
53 if (tcgetattr(fd, &saved_termios) < 0) {
54 return -1;
55 }
56 }
57
58 buf = saved_termios;
59 /*
60 * echo off, canonical mode off, extended input processing off,
61 * signal chars off
62 */
63 buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
64 /*
65 * no SIGINT on break, CR-to-NL off, input parity check off, do not
66 * strip 8th bit on input,output flow control off
67 */
68 buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
69 /* Clear size bits, parity checking off */
70 buf.c_cflag &= ~(CSIZE | PARENB);
71 /* set 8 bits/char */
72 buf.c_cflag |= CS8;
73 /* Output processing off
74 buf.c_oflag &= ~(OPOST); */
75
76 buf.c_cc[VMIN] = 1; /* one byte at a time, no timer */
77 buf.c_cc[VTIME] = 0;
78 if (tcsetattr(fd, TCSANOW, &buf) < 0) {
79 return -1;
80 } /* end of if(tcsetattr(fileno(stdin), TCSANOW, &buf) < 0) */
81 tty_state = RAW;
82 saved_fd = fd;
83 return 0;
84 }
85
86 void tty_atexit(void)
87 {
88 if (tty_state != CBREAK && tty_state != RAW) {
89 return;
90 }
91
92 if (tcsetattr(saved_fd, TCSANOW, &saved_termios) < 0) {
93 return;
94 } /* end of if(tcsetattr(fileno(stdin), TCSANOW, &buf) < 0) */
95 tty_state = RESET;
96 return;
97 }
98
99 int main(int argc, char *argv[])
100 {
101 pid_t child_pid;
102 struct termios tty_attr;
103 struct winsize window_size;
104 int pty_master;
105 int retval = 0;
106
107 /* for select */
108 fd_set readfds;
109 fd_set writefds;
110 fd_set exceptfds;
111
112 int err_count = 0;
113
114 /* for sigtimedwait() */
115 struct timespec timeout;
116 char buf[16384];
117
118 if (argc == 1) {
119 printf("usage: %s PROGRAM [ARGS]...\n", argv[0]);
120 exit(1);
121 }
122
123 sigset_t signal_set;
124 siginfo_t signalinfo;
125
126 /* set up SIGCHLD */
127 sigemptyset(&signal_set); /* no signals */
128 sigaddset(&signal_set, SIGCHLD); /* Add sig child */
129 sigprocmask(SIG_BLOCK, &signal_set, NULL); /* Block the signal */
130
131 /* Set both to 0, so sigtimed wait just does a poll */
132 timeout.tv_sec = 0;
133 timeout.tv_nsec = 0;
134
135 if (isatty(fileno(stdin))) {
136 /* get terminal parameters associated with stdout */
137 if (tcgetattr(fileno(stdout), &tty_attr) < 0) {
138 perror("tcgetattr:");
139 exit(EX_OSERR);
140 }
141
142 /* end of if(tcsetattr(&tty_attr)) */
143 /* get window size */
144 if (ioctl(fileno(stdout), TIOCGWINSZ, &window_size) < 0) {
145 perror("ioctl stdout:");
146 exit(1);
147 }
148
149 child_pid = forkpty(&pty_master, NULL, &tty_attr, &window_size);
150 } /* end of if(isatty(fileno(stdin))) */
151 else { /* not interactive */
152 child_pid = forkpty(&pty_master, NULL, NULL, NULL);
153 }
154
155 if (child_pid < 0) {
156 perror("forkpty():");
157 fflush(stdout);
158 fflush(stderr);
159 exit(EX_OSERR);
160 } /* end of if(child_pid < 0) */
161 if (child_pid == 0) {
162 /* in the child */
163 struct termios s_tty_attr;
164 if (tcgetattr(fileno(stdin), &s_tty_attr)) {
165 perror("Child:");
166 fflush(stdout);
167 fflush(stderr);
168 exit(EXIT_FAILURE);
169 }
170 /* Turn off echo */
171 s_tty_attr.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
172 /* Also turn of NL to CR?LF on output */
173 s_tty_attr.c_oflag &= ~(ONLCR);
174 if (tcsetattr(fileno(stdin), TCSANOW, &s_tty_attr)) {
175 perror("Child:");
176 exit(EXIT_FAILURE);
177 }
178 { /* There is no reason to block sigchild for the process we
179 shall exec here */
180 sigset_t chld_signal_set;
181 /* release SIGCHLD */
182 sigemptyset(&chld_signal_set); /* no signals */
183 sigaddset(&chld_signal_set, SIGCHLD); /* Add sig child */
184 sigprocmask(SIG_UNBLOCK, &chld_signal_set, NULL); /* Unblock the signal */
185 }
186
187 if (execvp(argv[1], argv + 1)) {
188 perror("Exec:");
189 fflush(stdout);
190 fflush(stderr);
191 exit(EXIT_FAILURE);
192 }
193 }
194
195 /* end of if(child_pid == 0) */
196 /*
197 * OK. Prepare to handle IO from the child. We need to transfer
198 * everything from the child's stdout to ours.
199 */
200 FD_ZERO(&readfds);
201 FD_ZERO(&writefds);
202 FD_ZERO(&exceptfds);
203
204 /*
205 * Read current file descriptor flags, preparing to do non blocking reads
206 */
207 retval = fcntl(pty_master, F_GETFL);
208 if (retval < 0) {
209 perror("fcntl_get");
210 fflush(stdout);
211 fflush(stderr);
212 exit(EX_IOERR);
213 }
214
215 /* Set the connection to be non-blocking */
216 if (fcntl(pty_master, F_SETFL, retval | O_NONBLOCK) < 0) {
217 perror("fcnt_setFlag_nonblock:");
218 fflush(stdout);
219 fflush(stderr);
220 exit(1);
221 }
222
223 FD_SET(pty_master, &readfds);
224 FD_SET(pty_master, &writefds);
225 FD_SET(fileno(stdin), &readfds);
226 if (isatty(fileno(stdin))) {
227 if (tty_semi_raw(fileno(stdin)) < 0) {
228 perror("Error: settingraw mode:");
229 fflush(stdout);
230 fflush(stderr);
231 } /* end of if(tty_raw(fileno(stdin)) < 0) */
232 if (atexit(tty_atexit) < 0) {
233 perror("Atexit setup:");
234 fflush(stdout);
235 fflush(stderr);
236 } /* end of if(atexit(tty_atexit) < 0) */
237 }
238
239 /* ignore return from nice, but lower our priority */
240 int ignore __attribute__ ((unused)) = nice(19);
241
242 /* while no signal, we loop around */
243 int done = 0;
244 while (!done) {
245 struct timeval interval;
246 fd_set t_readfds;
247 fd_set t_writefds;
248 fd_set t_exceptfds;
249 /*
250 * We still use a blocked signal, and check for SIGCHLD every
251 * loop, since waiting infinitely did not really help the load
252 * when running, say, top.
253 */
254 interval.tv_sec = 0;
255 interval.tv_usec = 200000; /* so, check for signals every 200 milli
256 seconds */
257
258 t_readfds = readfds;
259 t_writefds = writefds;
260 t_exceptfds = exceptfds;
261
262 /* check for the signal */
263 retval = sigtimedwait(&signal_set, &signalinfo, &timeout);
264
265 if (retval == SIGCHLD) {
266 /* child terminated */
267 done = 1; /* in case they do not close off their
268 file descriptors */
269 } else {
270 if (retval < 0) {
271 if (errno != EAGAIN) {
272 perror("sigtimedwait");
273 fflush(stdout);
274 fflush(stderr);
275 exit(EX_IOERR);
276 } else {
277 /* No signal in set was delivered within the timeout period specified */
278 }
279 }
280 } /* end of else */
281
282 if (select
283 (pty_master + 1, &t_readfds, &t_writefds, &t_exceptfds,
284 &interval) < 0) {
285 perror("Select:");
286 fflush(stdout);
287 fflush(stderr);
288 exit(EX_IOERR);
289 }
290
291 if (FD_ISSET(pty_master, &t_readfds)) {
292 retval = read(pty_master, buf, (unsigned int)16384);
293 if (retval < 0) {
294 if (errno != EINTR && errno != EAGAIN) { /* Nothing left to read? */
295 fflush(stdout);
296 fflush(stderr);
297 /* fprintf(stderr, "DEBUG: %d: Nothing left to read?\n", __LINE__); */
298 exit(EXIT_SUCCESS);
299 } /* end of else */
300 } /* end of if(retval < 0) */
301 else {
302 if (retval == 0) {
303 if (++err_count > 5) { /* child closed connection */
304 fflush(stdout);
305 fflush(stderr);
306 /*fprintf(stderr, "DEBUG: %d: child closed connection?\n", __LINE__); */
307 exit(EXIT_SUCCESS);
308 }
309 } /* end of if(retval == 0) */
310 else {
311 ssize_t nleft = retval;
312 ssize_t nwritten = 0;
313 char *ptr = buf;
314 while (nleft > 0) {
315 if ((nwritten =
316 write(fileno(stdout), ptr,
317 (unsigned int)nleft))
318 <= 0) {
319 if (errno == EINTR) {
320 nwritten = 0;
321 } /* end of if(errno == EINTR) */
322 else {
323 perror("write");
324 fflush(stdout);
325 fflush(stderr);
326 exit(EXIT_SUCCESS);
327 } /* end of else */
328 } /* end of if((nwritten = write(sockfd, ptr, nleft)) <= 0) */
329 nleft -= nwritten;
330 ptr += nwritten;
331 } /* end of while(nleft > 0) */
332
333 /* fprintf(stderr, "DEBUG: %d: wrote %d\n", __LINE__, retval); */
334 fflush(stdout);
335 } /* end of else */
336 } /* end of else */
337 }
338 if (FD_ISSET(fileno(stdin), &t_readfds)) {
339 if (FD_ISSET(pty_master, &t_writefds)) {
340 retval =
341 read(fileno(stdin), buf,
342 (unsigned int)16384);
343 if (retval < 0) {
344 if (errno != EINTR && errno != EAGAIN) { /* Nothing left to read? */
345 fflush(stdout);
346 fflush(stderr);
347 exit(EXIT_SUCCESS);
348 } /* end of else */
349 } /* end of if(retval < 0) */
350 else {
351 if (retval == 0) {
352 if (++err_count > 5) { /* lost controlling tty */
353 fflush(stdout);
354 fflush(stderr);
355 exit(EXIT_SUCCESS);
356 }
357 } /* end of if(retval == 0) */
358 else {
359 ssize_t nleft = retval;
360 ssize_t nwritten = 0;
361 char *ptr = buf;
362 while (nleft > 0) {
363 if ((nwritten =
364 write(pty_master,
365 ptr,
366 (unsigned
367 int)nleft))
368 <= 0) {
369 if (errno ==
370 EINTR) {
371 nwritten
372 = 0;
373 } /* end of if(errno == EINTR) */
374 else {
375 perror
376 ("write");
377 fflush
378 (stdout);
379 fflush
380 (stderr);
381 exit(EXIT_SUCCESS);
382 } /* end of else */
383 } /* end of if((nwritten = write(sockfd, ptr, nleft)) <= 0) */
384 nleft -= nwritten;
385 ptr += nwritten;
386 } /* end of while(nleft > 0) */
387
388 fflush(stdout);
389 } /* end of else */
390 } /* end of else */
391 } /* end of if(FD_ISSET(pty_master, &writefds)) */
392 } /* something to read on stdin */
393 } /* Loop */
394
395 fflush(stdout);
396 fflush(stderr);
397
398 exit(EXIT_SUCCESS);
399 } /* end of main() */
No issues found
1 /************************************************************************
2 *
3 * run_init
4 *
5 * SYNOPSIS:
6 *
7 * This program allows a user to run an /etc/init.d script in the proper context.
8 *
9 * USAGE:
10 *
11 * run_init <script> <args>
12 *
13 * BUILD OPTIONS:
14 *
15 * option USE_PAM:
16 *
17 * Set the USE_PAM constant if you want to authenticate users via PAM.
18 * If USE_PAM is not set, users will be authenticated via direct
19 * access to the shadow password file.
20 *
21 * If you decide to use PAM must be told how to handle run_init. A
22 * good rule-of-thumb might be to tell PAM to handle run_init in the
23 * same way it handles su, except that you should remove the pam_rootok.so
24 * entry so that even root must re-authenticate to run the init scripts
25 * in the proper context.
26 *
27 * If you choose not to use PAM, make sure you have a shadow passwd file
28 * in /etc/shadow. You can use a simlink if your shadow passwd file
29 * lives in another directory. Example:
30 * su
31 * cd /etc
32 * ln -s /etc/auth/shadow shadow
33 *
34 * If you decide not to use PAM, you will also have to make run_init
35 * setuid root, so that it can read the shadow passwd file.
36 *
37 *
38 *************************************************************************/
39
40 #include <stdio.h>
41 #include <stdlib.h> /* for malloc(), realloc(), free() */
42 #include <pwd.h> /* for getpwuid() */
43 #include <sys/types.h> /* to make getuid() and getpwuid() happy */
44 #include <sys/wait.h> /* for wait() */
45 #include <sys/stat.h> /* for struct stat and friends */
46 #include <getopt.h> /* for getopt_long() form of getopt() */
47 #include <selinux/selinux.h>
48 #include <selinux/get_default_type.h>
49 #include <selinux/context.h> /* for context-mangling functions */
50 #include <fcntl.h>
51 #include <ctype.h>
52 #include <limits.h>
53 #ifdef USE_AUDIT
54 #include <libaudit.h>
55 #endif
56 #ifdef USE_NLS
57 #include <libintl.h>
58 #include <locale.h>
59 #define _(msgid) gettext (msgid)
60 #else
61 #define _(msgid) (msgid)
62 #endif
63 #ifndef PACKAGE
64 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
65 #endif
66 /* USAGE_STRING describes the command-line args of this program. */
67 #define USAGE_STRING _("USAGE: run_init <script> <args ...>\n\
68 where: <script> is the name of the init script to run,\n\
69 <args ...> are the arguments to that script.")
70
71 #define CONTEXT_FILE "initrc_context"
72 #ifdef USE_PAM
73
74 /************************************************************************
75 *
76 * All PAM code goes in this section.
77 *
78 ************************************************************************/
79
80 #include <unistd.h> /* for getuid(), exit(), getopt() */
81
82 #include <security/pam_appl.h> /* for PAM functions */
83 #include <security/pam_misc.h> /* for misc_conv PAM utility function */
84
85 #define SERVICE_NAME "run_init" /* the name of this program for PAM */
86 /* The file containing the context to run
87 * the scripts under. */
88
89 int authenticate_via_pam(const struct passwd *);
90
91 /* authenticate_via_pam()
92 *
93 * in: p_passwd_line - struct containing data from our user's line in
94 * the passwd file.
95 * out: nothing
96 * return: value condition
97 * ----- ---------
98 * 1 PAM thinks that the user authenticated themselves properly
99 * 0 otherwise
100 *
101 * This function uses PAM to authenticate the user running this
102 * program. This is the only function in this program that makes PAM
103 * calls.
104 *
105 */
106
107 int authenticate_via_pam(const struct passwd *p_passwd_line)
108 {
109
110 int result = 0; /* our result, set to 0 (not authenticated) by default */
111 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
112
113 /* This is a jump table of functions for PAM to use when it wants to *
114 * communicate with the user. We'll be using misc_conv(), which is *
115 * provided for us via pam_misc.h. */
116 struct pam_conv pam_conversation = {
117 misc_conv,
118 NULL
119 };
120
121 /* Make `p_pam_handle' a valid PAM handle so we can use it when *
122 * calling PAM functions. */
123 if (PAM_SUCCESS != pam_start(SERVICE_NAME,
124 p_passwd_line->pw_name,
125 &pam_conversation, &pam_handle)) {
126 fprintf(stderr, _("failed to initialize PAM\n"));
127 exit(-1);
128 }
129
130 /* Ask PAM to authenticate the user running this program */
131 if (PAM_SUCCESS == pam_authenticate(pam_handle, 0)) {
132 result = 1; /* user authenticated OK! */
133 }
134
135 /* If we were successful, call pam_acct_mgmt() to reset the
136 * pam_tally failcount.
137 */
138 if (result && (PAM_SUCCESS != pam_acct_mgmt(pam_handle, 0)) ) {
139 fprintf(stderr, _("failed to get account information\n"));
140 exit(-1);
141 }
142
143 /* We're done with PAM. Free `pam_handle'. */
144 pam_end(pam_handle, PAM_SUCCESS);
145
146 return (result);
147
148 } /* authenticate_via_pam() */
149
150 #else /* else !USE_PAM */
151
152 /************************************************************************
153 *
154 * All shadow passwd code goes in this section.
155 *
156 ************************************************************************/
157
158 #include <unistd.h> /* for getuid(), exit(), crypt() */
159 #include <shadow.h> /* for shadow passwd functions */
160 #include <string.h> /* for strlen(), memset() */
161
162 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
163
164 int authenticate_via_shadow_passwd(const struct passwd *);
165
166 /* authenticate_via_shadow_passwd()
167 *
168 * in: p_passwd_line - struct containing data from our user's line in
169 * the passwd file.
170 * out: nothing
171 * return: value condition
172 * ----- ---------
173 * 1 user authenticated themselves properly according to the
174 * shadow passwd file.
175 * 0 otherwise
176 *
177 * This function uses the shadow passwd file to authenticate the user running
178 * this program.
179 *
180 */
181
182 int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
183 {
184
185 struct spwd *p_shadow_line; /* struct derived from shadow passwd file line */
186 char *unencrypted_password_s; /* unencrypted password input by user */
187 char *encrypted_password_s; /* user's password input after being crypt()ed */
188
189 /* Make `p_shadow_line' point to the data from the current user's *
190 * line in the shadow passwd file. */
191 setspent(); /* Begin access to the shadow passwd file. */
192 p_shadow_line = getspnam(p_passwd_line->pw_name);
193 endspent(); /* End access to the shadow passwd file. */
194 if (!(p_shadow_line)) {
195 fprintf(stderr,
196 _
197 ("Cannot find your entry in the shadow passwd file.\n"));
198 exit(-1);
199 }
200
201 /* Ask user to input unencrypted password */
202 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
203 fprintf(stderr, _("getpass cannot open /dev/tty\n"));
204 exit(-1);
205 }
206
207 /* Use crypt() to encrypt user's input password. Clear the *
208 * unencrypted password as soon as we're done, so it is not *
209 * visible to memory snoopers. */
210 encrypted_password_s = crypt(unencrypted_password_s,
211 p_shadow_line->sp_pwdp);
212 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
213
214 /* Return 1 (authenticated) iff the encrypted version of the user's *
215 * input password matches the encrypted password stored in the *
216 * shadow password file. */
217 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
218
219 } /* authenticate_via_shadow_passwd() */
220
221 #endif /* if/else USE_PAM */
222
223 /*
224 * authenticate_user()
225 *
226 * Authenticate the user.
227 *
228 * in: nothing
229 * out: nothing
230 * return: 0 When success
231 * -1 When failure
232 */
233 int authenticate_user()
234 {
235
236 #define INITLEN 255
237 struct passwd *p_passwd_line; /* struct derived from passwd file line */
238 uid_t uid;
239
240 /*
241 * Determine the Linux user identity to re-authenticate.
242 * If supported and set, use the login uid, as this should be more stable.
243 * Otherwise, use the real uid.
244 * The SELinux user identity is no longer used, as Linux users are now
245 * mapped to SELinux users via seusers and the SELinux user identity space
246 * is separate.
247 */
248 #ifdef USE_AUDIT
249 uid = audit_getloginuid();
250 if (uid == (uid_t) - 1)
251 uid = getuid();
252 #else
253 uid = getuid();
254 #endif
255
256 p_passwd_line = getpwuid(uid);
257 if (!p_passwd_line) {
258 fprintf(stderr, "cannot find your entry in the passwd file.\n");
259 return (-1);
260 }
261
262 printf("Authenticating %s.\n", p_passwd_line->pw_name);
263
264 /*
265 * Re-authenticate the user running this program.
266 * This is just to help confirm user intent (vs. invocation by
267 * malicious software), not to authorize the operation (which is covered
268 * by policy). Trusted path mechanism would be preferred.
269 */
270 #ifdef USE_PAM
271 if (!authenticate_via_pam(p_passwd_line)) {
272 #else /* !USE_PAM */
273 if (!authenticate_via_shadow_passwd(p_passwd_line)) {
274 #endif /* if/else USE_PAM */
275 fprintf(stderr, _("run_init: incorrect password for %s\n"),
276 p_passwd_line->pw_name);
277 return (-1);
278 }
279
280 /* If we reach here, then we have authenticated the user. */
281 #ifdef CANTSPELLGDB
282 printf("You are authenticated!\n");
283 #endif
284
285 return 0;
286
287 } /* authenticate_user() */
288
289 /*
290 * get_init_context()
291 *
292 * Get the CONTEXT associated with the context for the init scripts. *
293 *
294 * in: nothing
295 * out: The CONTEXT associated with the context.
296 * return: 0 on success, -1 on failure.
297 */
298 int get_init_context(security_context_t * context)
299 {
300
301 FILE *fp;
302 char buf[255], *bufp;
303 int buf_len;
304 char context_file[PATH_MAX];
305 snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
306 selinux_contexts_path(), CONTEXT_FILE);
307 fp = fopen(context_file, "r");
308 if (!fp) {
309 fprintf(stderr, _("Could not open file %s\n"), context_file);
310 return -1;
311 }
312
313 while (1) { /* loop until we find a non-empty line */
314
315 if (!fgets(buf, sizeof buf, fp))
316 break;
317
318 buf_len = strlen(buf);
319 if (buf[buf_len - 1] == '\n')
320 buf[buf_len - 1] = 0;
321
322 bufp = buf;
323 while (*bufp && isspace(*bufp))
324 bufp++;
325
326 if (*bufp) {
327 *context = strdup(bufp);
328 if (!(*context))
329 goto out;
330 fclose(fp);
331 return 0;
332 }
333 }
334 out:
335 fclose(fp);
336 fprintf(stderr, _("No context in file %s\n"), context_file);
337 return -1;
338
339 } /* get_init_context() */
340
341 /*****************************************************************************
342 * main() *
343 *****************************************************************************/
344 int main(int argc, char *argv[])
345 {
346
347 extern char *optarg; /* used by getopt() for arg strings */
348 extern int opterr; /* controls getopt() error messages */
349 security_context_t new_context; /* context for the init script context */
350
351 #ifdef USE_NLS
352 setlocale(LC_ALL, "");
353 bindtextdomain(PACKAGE, LOCALEDIR);
354 textdomain(PACKAGE);
355 #endif
356
357 /* Verify that we are running on a flask-enabled kernel. */
358 if (!is_selinux_enabled()) {
359 fprintf(stderr,
360 _
361 ("Sorry, run_init may be used only on a SELinux kernel.\n"));
362 exit(-1);
363 }
364
365 /*
366 * Step 1: Handle command-line arguments. The first argument is the
367 * name of the script to run. All other arguments are for the script
368 * itself, and will be passed directly to the script.
369 */
370
371 if (argc < 2) {
372 fprintf(stderr, "%s\n", USAGE_STRING);
373 exit(-1);
374 }
375
376 /*
377 * Step 2: Authenticate the user.
378 */
379 if (authenticate_user() != 0) {
380 fprintf(stderr, _("authentication failed.\n"));
381 exit(-1);
382 }
383
384 /*
385 * Step 3: Get the context for the script to be run in.
386 */
387 if (get_init_context(&new_context) == 0) {
388 #ifdef CANTSPELLGDB
389 printf("context is %s\n", new_context);
390 #endif
391 } else {
392 exit(-1);
393 }
394
395 /*
396 * Step 4: Run the command in the correct context.
397 */
398
399 if (chdir("/")) {
400 perror("chdir");
401 exit(-1);
402 }
403
404 if (setexeccon(new_context) < 0) {
405 fprintf(stderr, _("Could not set exec context to %s.\n"),
406 new_context);
407 exit(-1);
408 }
409 if (! access("/usr/sbin/open_init_pty", X_OK)) {
410 if (execvp(argv[1], argv + 1)) {
411 perror("execvp");
412 exit(-1);
413 }
414 return 0;
415 }
416 /*
417 * Do not execvp the command directly from run_init; since it would run
418 * under with a pty under sysadm_devpts_t. Instead, we call open_init_tty,
419 * which transitions us into initrc_t, which then spawns a new
420 * process, that gets a pty with context initrc_devpts_t. Just
421 * execvp or using a exec(1) recycles pty's, and does not open a new
422 * one.
423 */
424 if (execvp("/usr/sbin/open_init_pty", argv)) {
425 perror("execvp");
426 exit(-1);
427 }
428 return 0;
429
430 } /* main() */
Location | Tool | Test ID | Issue | |
---|---|---|---|---|
seunshare.c:271:11 | clang-analyzer | The left operand to '|' is always 0 | ||
seunshare.c:791:0 | cppcheck | memleakOnRealloc | Common realloc mistake: 'pid_table' nulled but not freed upon failure |
1 /*
2 * Authors: Dan Walsh <dwalsh@redhat.com>
3 * Authors: Thomas Liu <tliu@fedoraproject.org>
4 */
5
6 #define _GNU_SOURCE
7 #include <signal.h>
8 #include <sys/fsuid.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <syslog.h>
13 #include <sys/mount.h>
14 #include <glob.h>
15 #include <pwd.h>
16 #include <sched.h>
17 #include <libcgroup.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <regex.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <cap-ng.h>
24 #include <getopt.h> /* for getopt_long() form of getopt() */
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <fcntl.h>
29
30 #include <selinux/selinux.h>
31 #include <selinux/context.h> /* for context-mangling functions */
32 #include <dirent.h>
33
34
35 /*
36 * Note setfsuid never returns an error code. But the compiler complains if
37 * I do not check, so I am checking for -1, which should never happen.
38 */
39
40 #ifdef USE_NLS
41 #include <locale.h> /* for setlocale() */
42 #include <libintl.h> /* for gettext() */
43 #define _(msgid) gettext (msgid)
44 #else
45 #define _(msgid) (msgid)
46 #endif
47
48 #ifndef MS_REC
49 #define MS_REC 1<<14
50 #endif
51
52 #ifndef MS_SLAVE
53 #define MS_SLAVE 1<<19
54 #endif
55
56 #ifndef PACKAGE
57 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
58 #endif
59
60 #define BUF_SIZE 1024
61 #define DEFAULT_PATH "/usr/bin:/bin"
62 #define USAGE_STRING _("USAGE: seunshare [ -v ] [ -C ] [ -c ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -Z CONTEXT ] -- executable [args] ")
63
64 static int verbose = 0;
65 static int child = 0;
66
67 static capng_select_t cap_set = CAPNG_SELECT_CAPS;
68
69 /**
70 * This function will drop all capabilities.
71 */
72 static int drop_caps()
73 {
74 if (capng_have_capabilities(cap_set) == CAPNG_NONE)
75 return 0;
76 capng_clear(cap_set);
77 if (capng_lock() == -1 || capng_apply(cap_set) == -1) {
78 fprintf(stderr, _("Failed to drop all capabilities\n"));
79 return -1;
80 }
81 return 0;
82 }
83
84 /**
85 * This function will drop all privileges.
86 */
87 static int drop_privs(uid_t uid)
88 {
89 if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) {
90 fprintf(stderr, _("Failed to drop privileges\n"));
91 return -1;
92 }
93 return 0;
94 }
95
96 /**
97 * If the user sends a siginto to seunshare, kill the child's session
98 */
99 void handler(int sig) {
100 if (child > 0) kill(-child,sig);
101 }
102
103 /**
104 * Take care of any signal setup.
105 */
106 static int set_signal_handles(void)
107 {
108 sigset_t empty;
109
110 /* Empty the signal mask in case someone is blocking a signal */
111 if (sigemptyset(&empty)) {
112 fprintf(stderr, "Unable to obtain empty signal set\n");
113 return -1;
114 }
115
116 (void)sigprocmask(SIG_SETMASK, &empty, NULL);
117
118 /* Terminate on SIGHUP */
119 if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
120 perror("Unable to set SIGHUP handler");
121 return -1;
122 }
123
124 if (signal(SIGINT, handler) == SIG_ERR) {
125 perror("Unable to set SIGINT handler");
126 return -1;
127 }
128
129 return 0;
130 }
131
132 #define status_to_retval(status,retval) do { \
133 if ((status) == -1) \
134 retval = -1; \
135 else if (WIFEXITED((status))) \
136 retval = WEXITSTATUS((status)); \
137 else if (WIFSIGNALED((status))) \
138 retval = 128 + WTERMSIG((status)); \
139 else \
140 retval = -1; \
141 } while(0)
142
143 /**
144 * Spawn external command using system() with dropped privileges.
145 * TODO: avoid system() and use exec*() instead
146 */
147 static int spawn_command(const char *cmd, uid_t uid){
148 int child;
149 int status = -1;
150
151 if (verbose > 1)
152 printf("spawn_command: %s\n", cmd);
153
154 child = fork();
155 if (child == -1) {
156 perror(_("Unable to fork"));
157 return status;
158 }
159
160 if (child == 0) {
161 if (drop_privs(uid) != 0) exit(-1);
162
163 status = system(cmd);
164 status_to_retval(status, status);
165 exit(status);
166 }
167
168 waitpid(child, &status, 0);
169 status_to_retval(status, status);
170 return status;
171 }
172
173 /**
174 * Check file/directory ownership, struct stat * must be passed to the
175 * functions.
176 */
177 static int check_owner_uid(uid_t uid, const char *file, struct stat *st) {
178 if (S_ISLNK(st->st_mode)) {
179 fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
180 return -1;
181 }
182 if (st->st_uid != uid) {
183 fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid);
184 return -1;
185 }
186 return 0;
187 }
188
189 static int check_owner_gid(gid_t gid, const char *file, struct stat *st) {
190 if (S_ISLNK(st->st_mode)) {
191 fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
192 return -1;
193 }
194 if (st->st_gid != gid) {
195 fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid);
196 return -1;
197 }
198 return 0;
199 }
200
201 #define equal_stats(one,two) \
202 ((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \
203 (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \
204 (one)->st_mode == (two)->st_mode)
205
206 /**
207 * Sanity check specified directory. Store stat info for future comparison, or
208 * compare with previously saved info to detect replaced directories.
209 * Note: This function does not perform owner checks.
210 */
211 static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) {
212 struct stat sb;
213
214 if (st_out == NULL) st_out = &sb;
215
216 if (lstat(dir, st_out) == -1) {
217 fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno));
218 return -1;
219 }
220 if (! S_ISDIR(st_out->st_mode)) {
221 fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno));
222 return -1;
223 }
224 if (st_in && !equal_stats(st_in, st_out)) {
225 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir);
226 return -1;
227 }
228
229 return 0;
230 }
231
232 /**
233 * This function checks to see if the shell is known in /etc/shells.
234 * If so, it returns 0. On error or illegal shell, it returns -1.
235 */
236 static int verify_shell(const char *shell_name)
237 {
238 int rc = -1;
239 const char *buf;
240
241 if (!(shell_name && shell_name[0]))
242 return rc;
243
244 while ((buf = getusershell()) != NULL) {
245 /* ignore comments */
246 if (*buf == '#')
247 continue;
248
249 /* check the shell skipping newline char */
250 if (!strcmp(shell_name, buf)) {
251 rc = 0;
252 break;
253 }
254 }
255 endusershell();
256 return rc;
257 }
258
259 /**
260 * Mount directory and check that we mounted the right directory.
261 */
262 static int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
263 {
264 int flags = 0;
265 int is_tmp = 0;
266
267 if (verbose)
268 printf(_("Mounting %s on %s\n"), src, dst);
269
270 if (strcmp("/tmp", dst) == 0) {
271 flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
272 is_tmp = 1;
273 }
274
275 /* mount directory */
276 if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
277 fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
278 return -1;
279 }
280
281 /* verify whether we mounted what we expected to mount */
282 if (verify_directory(dst, src_st, NULL) < 0) return -1;
283
284 /* bind mount /tmp on /var/tmp too */
285 if (is_tmp) {
286 if (verbose)
287 printf(_("Mounting /tmp on /var/tmp\n"));
288
289 if (mount("/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) {
290 fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
291 return -1;
292 }
293 }
294
295 return 0;
296
297 }
298
299 /**
300 * Error logging used by cgroups code.
301 */
302 static int sandbox_error(const char *string)
303 {
304 fprintf(stderr, "%s", string);
305 syslog(LOG_AUTHPRIV | LOG_ALERT, "%s", string);
306 exit(-1);
307 }
308
309 /**
310 * Regular expression match.
311 */
312 static int match(const char *string, char *pattern)
313 {
314 int status;
315 regex_t re;
316 if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
317 return 0;
318 }
319 status = regexec(&re, string, (size_t)0, NULL, 0);
320 regfree(&re);
321 if (status != 0) {
322 return 0;
323 }
324 return 1;
325 }
326
327 /**
328 * Apply cgroups settings from the /etc/sysconfig/sandbox config file.
329 */
330 static int setup_cgroups()
331 {
332 char *cpus = NULL; /* which CPUs to use */
333 char *cgroupname = NULL;/* name for the cgroup */
334 char *mem = NULL; /* string for memory amount to pass to cgroup */
335 int64_t memusage = 0; /* amount of memory to use max (percent) */
336 int cpupercentage = 0; /* what percentage of cpu to allow usage */
337 FILE* fp;
338 char buf[BUF_SIZE];
339 char *tok = NULL;
340 int rc = -1;
341 char *str = NULL;
342 const char* fname = "/etc/sysconfig/sandbox";
343
344 if ((fp = fopen(fname, "rt")) == NULL) {
345 fprintf(stderr, "Error opening sandbox config file.");
346 return rc;
347 }
348 while(fgets(buf, BUF_SIZE, fp) != NULL) {
349 /* Skip comments */
350 if (buf[0] == '#') continue;
351
352 /* Copy the string, ignoring whitespace */
353 int len = strlen(buf);
354 free(str);
355 str = malloc((len + 1) * sizeof(char));
356 if (!str)
357 goto err;
358
359 int ind = 0;
360 int i;
361 for (i = 0; i < len; i++) {
362 char cur = buf[i];
363 if (cur != ' ' && cur != '\t') {
364 str[ind] = cur;
365 ind++;
366 }
367 }
368 str[ind] = '\0';
369
370 tok = strtok(str, "=\n");
371 if (tok != NULL) {
372 if (!strcmp(tok, "CPUAFFINITY")) {
373 tok = strtok(NULL, "=\n");
374 cpus = strdup(tok);
375 if (!strcmp(cpus, "ALL")) {
376 free(cpus);
377 cpus = NULL;
378 }
379 } else if (!strcmp(tok, "MEMUSAGE")) {
380 tok = strtok(NULL, "=\n");
381 if (match(tok, "^[0-9]+[kKmMgG%]")) {
382 char *ind = strchr(tok, '%');
383 if (ind != NULL) {
384 *ind = '\0';;
385 memusage = atoi(tok);
386 } else {
387 mem = strdup(tok);
388 }
389 } else {
390 fprintf(stderr, "Error parsing config file.");
391 goto err;
392 }
393
394 } else if (!strcmp(tok, "CPUUSAGE")) {
395 tok = strtok(NULL, "=\n");
396 if (match(tok, "^[0-9]+\%")) {
397 char* ind = strchr(tok, '%');
398 *ind = '\0';
399 cpupercentage = atoi(tok);
400 } else {
401 fprintf(stderr, "Error parsing config file.");
402 goto err;
403 }
404 } else if (!strcmp(tok, "NAME")) {
405 tok = strtok(NULL, "=\n");
406 cgroupname = strdup(tok);
407 } else {
408 continue;
409 }
410 }
411
412 }
413 if (mem == NULL) {
414 long phypz = sysconf(_SC_PHYS_PAGES);
415 long psize = sysconf(_SC_PAGE_SIZE);
416 memusage = phypz * psize * (float) memusage / 100.0;
417 }
418
419 cgroup_init();
420
421 int64_t current_runtime = 0;
422 int64_t current_period = 0 ;
423 int64_t current_mem = 0;
424 char *curr_cpu_path = NULL;
425 char *curr_mem_path = NULL;
426 int ret = cgroup_get_current_controller_path(getpid(), "cpu", &curr_cpu_path);
427 if (ret) {
428 sandbox_error("Error while trying to get current controller path.\n");
429 } else {
430 struct cgroup *curr = cgroup_new_cgroup(curr_cpu_path);
431 cgroup_get_cgroup(curr);
432 cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_runtime_us", ¤t_runtime);
433 cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_period_us", ¤t_period);
434 }
435
436 ret = cgroup_get_current_controller_path(getpid(), "memory", &curr_mem_path);
437 if (ret) {
438 sandbox_error("Error while trying to get current controller path.\n");
439 } else {
440 struct cgroup *curr = cgroup_new_cgroup(curr_mem_path);
441 cgroup_get_cgroup(curr);
442 cgroup_get_value_int64(cgroup_get_controller(curr, "memory"), "memory.limit_in_bytes", ¤t_mem);
443 }
444
445 if (((float) cpupercentage) / 100.0> (float)current_runtime / (float) current_period) {
446 sandbox_error("CPU usage restricted!\n");
447 goto err;
448 }
449
450 if (mem == NULL) {
451 if (memusage > current_mem) {
452 sandbox_error("Attempting to use more memory than allowed!");
453 goto err;
454 }
455 }
456
457 long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
458
459 struct sched_param sp;
460 sp.sched_priority = sched_get_priority_min(SCHED_FIFO);
461 sched_setscheduler(getpid(), SCHED_FIFO, &sp);
462 struct cgroup *sandbox_group = cgroup_new_cgroup(cgroupname);
463 cgroup_add_controller(sandbox_group, "memory");
464 cgroup_add_controller(sandbox_group, "cpu");
465
466 if (mem == NULL) {
467 if (memusage > 0) {
468 cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", memusage);
469 }
470 } else {
471 cgroup_set_value_string(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", mem);
472 }
473 if (cpupercentage > 0) {
474 cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_runtime_us",
475 (float) cpupercentage / 100.0 * 60000);
476 cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_period_us",60000 * nprocs);
477 }
478 if (cpus != NULL) {
479 cgroup_set_value_string(cgroup_get_controller(sandbox_group, "cpu"), "cgroup.procs",cpus);
480 }
481
482 uint64_t allocated_mem;
483 if (cgroup_get_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", &allocated_mem) > current_mem) {
484 sandbox_error("Attempting to use more memory than allowed!\n");
485 goto err;
486 }
487
488 rc = cgroup_create_cgroup(sandbox_group, 1);
489 if (rc != 0) {
490 sandbox_error("Failed to create group. Ensure that cgconfig service is running. \n");
491 goto err;
492 }
493
494 cgroup_attach_task(sandbox_group);
495
496 rc = 0;
497 err:
498 fclose(fp);
499 free(str);
500 free(mem);
501 free(cgroupname);
502 free(cpus);
503 return rc;
504 }
505
506 /*
507 If path is empy or ends with "/." or "/.. return -1 else return 0;
508 */
509 static int bad_path(const char *path) {
510 const char *ptr;
511 ptr = path;
512 while (*ptr) ptr++;
513 if (ptr == path) return -1; // ptr null
514 ptr--;
515 if (ptr != path && *ptr == '.') {
516 ptr--;
517 if (*ptr == '/') return -1; // path ends in /.
518 if (*ptr == '.') {
519 if (ptr != path) {
520 ptr--;
521 if (*ptr == '/') return -1; // path ends in /..
522 }
523 }
524 }
525 return 0;
526 }
527
528 static int rsynccmd(const char * src, const char *dst, char **cmdbuf)
529 {
530 char *buf = NULL;
531 char *newbuf = NULL;
532 glob_t fglob;
533 fglob.gl_offs = 0;
534 int flags = GLOB_PERIOD;
535 unsigned int i = 0;
536 int rc = -1;
537
538 /* match glob for all files in src dir */
539 if (asprintf(&buf, "%s/*", src) == -1) {
540 fprintf(stderr, "Out of memory\n");
541 return -1;
542 }
543
544 if (glob(buf, flags, NULL, &fglob) != 0) {
545 free(buf); buf = NULL;
546 return -1;
547 }
548
549 free(buf); buf = NULL;
550
551 for ( i=0; i < fglob.gl_pathc; i++) {
552 const char *path = fglob.gl_pathv[i];
553
554 if (bad_path(path)) continue;
555
556 if (!buf) {
557 if (asprintf(&newbuf, "\'%s\'", path) == -1) {
558 fprintf(stderr, "Out of memory\n");
559 goto err;
560 }
561 } else {
562 if (asprintf(&newbuf, "%s \'%s\'", buf, path) == -1) {
563 fprintf(stderr, "Out of memory\n");
564 goto err;
565 }
566 }
567
568 free(buf); buf = newbuf;
569 newbuf = NULL;
570 }
571
572 if (buf) {
573 if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
574 fprintf(stderr, "Out of memory\n");
575 goto err;
576 }
577 *cmdbuf=newbuf;
578 }
579 else {
580 *cmdbuf=NULL;
581 }
582 rc = 0;
583
584 err:
585 free(buf); buf = NULL;
586 globfree(&fglob);
587 return rc;
588 }
589
590 /**
591 * Clean up runtime temporary directory. Returns 0 if no problem was detected,
592 * >0 if some error was detected, but errors here are treated as non-fatal and
593 * left to tmpwatch to finish incomplete cleanup.
594 */
595 static int cleanup_tmpdir(const char *tmpdir, const char *src,
596 struct passwd *pwd, int copy_content)
597 {
598 char *cmdbuf = NULL;
599 int rc = 0;
600
601 /* rsync files back */
602 if (copy_content) {
603 if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
604 fprintf(stderr, _("Out of memory\n"));
605 cmdbuf = NULL;
606 rc++;
607 }
608 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
609 fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
610 rc++;
611 }
612 free(cmdbuf); cmdbuf = NULL;
613 }
614
615 /* remove files from the runtime temporary directory */
616 if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
617 fprintf(stderr, _("Out of memory\n"));
618 cmdbuf = NULL;
619 rc++;
620 }
621 /* this may fail if there's root-owned file left in the runtime tmpdir */
622 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
623 free(cmdbuf); cmdbuf = NULL;
624
625 /* remove runtime temporary directory */
626 if (setfsuid(0) < 0)
627 rc++;
628
629 if (rmdir(tmpdir) == -1)
630 fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
631 if (setfsuid(pwd->pw_uid) < 0)
632 rc++;
633
634 return rc;
635 }
636
637 /**
638 * seunshare will create a tmpdir in /tmp, with root ownership. The parent
639 * process waits for it child to exit to attempt to remove the directory. If
640 * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch
641 * to clean it up.
642 */
643 static char *create_tmpdir(const char *src, struct stat *src_st,
644 struct stat *out_st, struct passwd *pwd, security_context_t execcon)
645 {
646 char *tmpdir = NULL;
647 char *cmdbuf = NULL;
648 int fd_t = -1, fd_s = -1;
649 struct stat tmp_st;
650 security_context_t con = NULL;
651
652 /* get selinux context */
653 if (execcon) {
654 if (setfsuid(pwd->pw_uid) < 0)
655 goto err;
656
657 if ((fd_s = open(src, O_RDONLY)) < 0) {
658 fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
659 goto err;
660 }
661 if (fstat(fd_s, &tmp_st) == -1) {
662 fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
663 goto err;
664 }
665 if (!equal_stats(src_st, &tmp_st)) {
666 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
667 goto err;
668 }
669 if (fgetfilecon(fd_s, &con) == -1) {
670 fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno));
671 goto err;
672 }
673
674 /* ok to not reach this if there is an error */
675 if (setfsuid(0) < 0)
676 goto err;
677 }
678
679 if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) {
680 fprintf(stderr, _("Out of memory\n"));
681 tmpdir = NULL;
682 goto err;
683 }
684 if (mkdtemp(tmpdir) == NULL) {
685 fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno));
686 goto err;
687 }
688
689 /* temporary directory must be owned by root:user */
690 if (verify_directory(tmpdir, NULL, out_st) < 0) {
691 goto err;
692 }
693
694 if (check_owner_uid(0, tmpdir, out_st) < 0)
695 goto err;
696
697 if (check_owner_gid(getgid(), tmpdir, out_st) < 0)
698 goto err;
699
700 /* change permissions of the temporary directory */
701 if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
702 fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
703 goto err;
704 }
705 if (fstat(fd_t, &tmp_st) == -1) {
706 fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
707 goto err;
708 }
709 if (!equal_stats(out_st, &tmp_st)) {
710 fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
711 goto err;
712 }
713 if (fchmod(fd_t, 01770) == -1) {
714 fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
715 goto err;
716 }
717 /* re-stat again to pick change mode */
718 if (fstat(fd_t, out_st) == -1) {
719 fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
720 goto err;
721 }
722
723 /* copy selinux context */
724 if (execcon) {
725 if (fsetfilecon(fd_t, con) == -1) {
726 fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno));
727 goto err;
728 }
729 }
730
731 if (setfsuid(pwd->pw_uid) < 0)
732 goto err;
733
734 if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
735 goto err;
736 }
737
738 /* ok to not reach this if there is an error */
739 if (setfsuid(0) < 0)
740 goto err;
741
742 if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
743 fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
744 cleanup_tmpdir(tmpdir, src, pwd, 0);
745 goto err;
746 }
747
748 goto good;
749 err:
750 free(tmpdir); tmpdir = NULL;
751 good:
752 free(cmdbuf); cmdbuf = NULL;
753 freecon(con); con = NULL;
754 if (fd_t >= 0) close(fd_t);
755 if (fd_s >= 0) close(fd_s);
756 return tmpdir;
757 }
758
759 #define PROC_BASE "/proc"
760
761 static int
762 killall (security_context_t execcon)
763 {
764 DIR *dir;
765 security_context_t scon;
766 struct dirent *de;
767 pid_t *pid_table, pid, self;
768 int i;
769 int pids, max_pids;
770 int running = 0;
771 self = getpid();
772 if (!(dir = opendir(PROC_BASE))) {
773 return -1;
774 }
775 max_pids = 256;
776 pid_table = malloc(max_pids * sizeof (pid_t));
777 if (!pid_table) {
778 (void)closedir(dir);
779 return -1;
780 }
781 pids = 0;
782 context_t con;
783 con = context_new(execcon);
784 const char *mcs = context_range_get(con);