1: /*************************************************************************
2: * (C) 2009 AITNET - Sofia/Bulgaria - <office@aitbg.com>
3: * by Michael Pounov <misho@aitbg.com>
4: *
5: * $Author: misho $
6: * $Id: cfexec.c,v 1.5.20.10 2021/03/21 01:30:19 misho Exp $
7: *
8: *************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004 - 2021
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: cfg_root_t cfg;
50: int Verbose, Timeout, kq;
51: ait_val_t User, Mount, Chroot;
52: char szSess[MAXPATHLEN], szSLCK[MAXPATHLEN], szConfig[MAXPATHLEN];
53: extern char compiled[], compiledby[], compilehost[];
54:
55:
56: static void
57: Usage()
58: {
59:
60: printf( "CFExec is tool for managment R/W operation with CompactFlash\n"
61: "=== %s === %s@%s ===\n\n"
62: " Syntax: cfexec [options] [exec_file]\n\n"
63: "\t-v\t\tVerbose ...\n"
64: "\t-C <config>\tLoad config [default=/etc/cfexec.conf]\n"
65: "\t-c <dir>\tAfter execute chroot to dir [default=/]\n"
66: "\t-u <user>\tAfter execute suid to user [default=root]\n"
67: "\t-m <mnt>\tOther mount dir [default=/]\n"
68: "\t-t <sec>\tTimeout for autolock mount dir after seconds [default=300]\n"
69: "\t-L <reason>\tService lock and set RW state of device with reason\n"
70: "\t-U \t\tService unlock and set RO state of device\n"
71: "\n", compiled, compiledby, compilehost);
72: }
73:
74: struct statfs *
75: getmntpt(const char *name)
76: {
77: struct statfs *mntbuf;
78: int i, mntsize;
79:
80: mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
81: for (i = mntsize - 1; i >= 0; i--) {
82: if (!strcmp(mntbuf[i].f_mntfromname, name) ||
83: !strcmp(mntbuf[i].f_mntonname, name))
84: return &mntbuf[i];
85: }
86:
87: return NULL;
88: }
89:
90: static int
91: update(int flags)
92: {
93: struct statfs *mntfs = getmntpt(AIT_GET_STR(&Mount));
94: char errmsg[STRSIZ] = { [0 ... STRSIZ - 1] = 0 };
95:
96: #ifdef __NetBSD__
97: struct ufs_args mnt;
98:
99: memset(&mnt, 0, sizeof mnt);
100: mnt.fspec = mntfs->f_mntfromname;
101: if (mount(mntfs->f_fstypename, mntfs->f_mntonname, flags, &mnt, sizeof mnt) == -1) {
102: #else
103: iovec_t *iov;
104: int ret;
105:
106: iov = iov_Init();
107: if (!iov)
108: return -1;
109: if (flags & MNT_RDONLY)
110: iov_PushPair(iov, "ro", "", 0);
111: else
112: iov_PushPair(iov, "noro", "", 0);
113: iov_PushPair(iov, "update", "", 0);
114: iov_PushPair(iov, "fstype", mntfs->f_fstypename, (size_t) -1);
115: iov_PushPair(iov, "fspath", mntfs->f_mntonname, (size_t) -1);
116: iov_PushPair(iov, "from", mntfs->f_mntfromname, (size_t) -1);
117: iov_PushPair(iov, "errmsg", errmsg, sizeof errmsg);
118:
119: ret = nmount(iov_Array(iov), iov_Size(iov), flags);
120:
121: iov_FreePairs(iov, 0);
122: iov_Destroy(&iov);
123: if (ret == -1) {
124: #endif
125: printf("Error:: can`t update mount %s %s #%d - %s\n", AIT_GET_STR(&Mount),
126: errmsg, errno, strerror(errno));
127: return -1;
128: }
129:
130: VERB(5) printf("Info(5):: safe mount %s for device %s to %s operation (%s)\n",
131: mntfs->f_fstypename, mntfs->f_mntfromname, mntfs->f_mntonname,
132: (flags & MNT_RDONLY) ? "ro" : "rw");
133: return 0;
134: }
135:
136: static void
137: setuser()
138: {
139: struct passwd *pw;
140:
141: pw = getpwnam(AIT_GET_LIKE(&User, char*));
142: if (pw) {
143: setuid(pw->pw_uid);
144: setgid(pw->pw_gid);
145: endpwent();
146:
147: VERB(5) printf("Info(5):: Suid to user %s.\n", AIT_GET_STR(&User));
148: } else
149: VERB(5) printf("Info(5):: Can`t suid to user %s !\n", AIT_GET_STR(&User));
150: }
151:
152: static int
153: mkevent(struct kevent *chg, struct kevent *evt)
154: {
155: int f;
156: char szStr[STRSIZ];
157:
158: f = open(szSess, O_CREAT | O_WRONLY | O_TRUNC, 0644);
159: if (f == -1) {
160: printf("Error:: can`t lock session #%d - %s\n", errno, strerror(errno));
161: return -1;
162: } else {
163: memset(szStr, 0, sizeof szStr);
164: snprintf(szStr, sizeof szStr, "%d", getpid());
165: write(f, szStr, strlen(szStr));
166: }
167: VERB(3) printf("Created lock file %s\n", szSess);
168:
169: if (chg && evt) {
170: kq = kqueue();
171: if (kq == -1) {
172: printf("Error:: can`t execute safe mount #%d - %s\n", errno, strerror(errno));
173: close(f);
174: unlink(szSess);
175: return -1;
176: } else {
177: memset(chg, 0, sizeof(struct kevent));
178: memset(evt, 0, sizeof(struct kevent));
179:
180: EV_SET(chg, f, EVFILT_VNODE, EV_ADD, NOTE_DELETE | NOTE_RENAME | NOTE_REVOKE,
181: 0, NULL);
182: }
183: }
184:
185: return f;
186: }
187:
188: static void
189: cleanexit()
190: {
191: AIT_FREE_VAL(&User);
192: AIT_FREE_VAL(&Mount);
193: AIT_FREE_VAL(&Chroot);
194: }
195:
196: static int
197: s_unlck()
198: {
199: if (access(szSLCK, F_OK))
200: return 2;
201:
202: if (access(szSess, F_OK) && update(MNT_UPDATE | MNT_RDONLY) == -1)
203: return 8;
204:
205: unlink(szSLCK);
206: VERB(3) printf("Unlock & deleted service lock file %s\n", szSLCK);
207: return 0;
208: }
209:
210: static int
211: s_lck(const char *reason)
212: {
213: int f;
214: char szStr[STRSIZ];
215:
216: if (!access(szSLCK, F_OK)) {
217: printf("cfexec already held service lock ...\n");
218: return 127;
219: }
220:
221: f = open(szSLCK, O_CREAT | O_WRONLY | O_TRUNC, 0644);
222: if (f == -1) {
223: printf("Error:: can`t service lock session #%d - %s\n", errno, strerror(errno));
224: return 4;
225: } else {
226: memset(szStr, 0, sizeof szStr);
227: snprintf(szStr, sizeof szStr, "[%d] - %s", getpid(), reason);
228: write(f, szStr, strlen(szStr));
229: }
230: close(f);
231:
232: if (update(MNT_UPDATE) == -1) {
233: unlink(szSLCK);
234: return 5;
235: }
236:
237: VERB(3) printf("Lock & created service lock file %s\n", szSLCK);
238: return 0;
239: }
240:
241:
242: int
243: main(int argc, char **argv)
244: {
245: char ch, mod = 0, reason[STRSIZ];
246: const char *err = NULL;
247: struct kevent chg, evt;
248: struct timespec ts;
249: pid_t pid;
250: int f, ret = 0, stat = 0;
251:
252: strlcpy(szConfig, DEFAULT_CONFIG, MAXPATHLEN);
253: /* Load variables from config if exists */
254: if (!cfgLoadConfig(szConfig, &cfg)) {
255: cfg_loadAttribute(&cfg, "cfexec", "timeout", &User, DEFAULT_TIMEOUT);
256: #ifndef HAVE_STRTONUM
257: Timeout = (int) strtol(szUser, NULL, 0);
258: #else
259: Timeout = strtonum(AIT_GET_STR(&User), 0, 3600, &err);
260: #endif
261: AIT_FREE_VAL(&User);
262: if (!Timeout && err) {
263: printf("Error:: in seconds for timeout %s - %s\n", optarg, err);
264: cfgUnloadConfig(&cfg);
265: return 1;
266: }
267: cfg_loadAttribute(&cfg, "cfexec", "suid", &User, DEFAULT_USER);
268: cfg_loadAttribute(&cfg, "cfexec", "mount", &Mount, DEFAULT_MOUNT);
269: cfg_loadAttribute(&cfg, "cfexec", "chroot", &Chroot, DEFAULT_CHROOT);
270:
271: cfgUnloadConfig(&cfg);
272: } else {
273: Timeout = atoi(DEFAULT_TIMEOUT);
274: AIT_SET_STR(&User, DEFAULT_USER);
275: AIT_SET_STR(&Mount, DEFAULT_MOUNT);
276: AIT_SET_STR(&Chroot, DEFAULT_CHROOT);
277: }
278:
279: atexit(cleanexit);
280:
281: /* Load variables from arguments if exists */
282: while ((ch = getopt(argc, argv, "hvUC:u:c:m:t:L:")) != -1)
283: switch (ch) {
284: case 'C':
285: if (!cfgLoadConfig(optarg, &cfg)) {
286: cfg_loadAttribute(&cfg, "cfexec", "timeout", &User, DEFAULT_TIMEOUT);
287: #ifndef HAVE_STRTONUM
288: Timeout = (int) strtol(szUser, NULL, 0);
289: #else
290: Timeout = strtonum(AIT_GET_STR(&User), 0, 3600, &err);
291: #endif
292: AIT_FREE_VAL(&User);
293: if (!Timeout && err) {
294: printf("Error:: in seconds for timeout %s - %s\n", optarg, err);
295: cfgUnloadConfig(&cfg);
296: return 1;
297: }
298: cfg_loadAttribute(&cfg, "cfexec", "suid", &User, DEFAULT_USER);
299: cfg_loadAttribute(&cfg, "cfexec", "mount", &Mount, DEFAULT_MOUNT);
300: cfg_loadAttribute(&cfg, "cfexec", "chroot", &Chroot, DEFAULT_CHROOT);
301:
302: cfgUnloadConfig(&cfg);
303: }
304: break;
305: case 'v':
306: Verbose++;
307: break;
308: case 'u':
309: AIT_SET_STR(&User, optarg);
310: break;
311: case 'c':
312: AIT_SET_STR(&Chroot, optarg);
313: break;
314: case 'm':
315: AIT_SET_STR(&Mount, optarg);
316: break;
317: case 'L':
318: strlcpy(reason, optarg, sizeof reason);
319: mod = 1;
320: break;
321: case 'U':
322: mod = -1;
323: break;
324: case 't':
325: #ifndef HAVE_STRTONUM
326: Timeout = (int) strtol(optarg, NULL, 0);
327: #else
328: Timeout = strtonum(optarg, 0, 3600, &err);
329: #endif
330: if (!Timeout && err) {
331: printf("Error:: in seconds for timeout %s - %s\n",
332: optarg, err);
333: return 1;
334: }
335: break;
336: case 'h':
337: default:
338: Usage();
339: return 1;
340: }
341: argc -= optind;
342: argv += optind;
343:
344: memset(szSess, 0, MAXPATHLEN);
345: snprintf(szSess, MAXPATHLEN, "%s%s-cfexec.LCK", DEFAULT_TMP, AIT_GET_STR(&Mount));
346: memset(szSLCK, 0, MAXPATHLEN);
347: snprintf(szSLCK, MAXPATHLEN, "%s%s-SYS-cfexec.LCK", DEFAULT_TMP, AIT_GET_STR(&Mount));
348:
349: /* we have request for service lock! */
350: if (mod) {
351: VERB(3) printf("Info(3):: mode=%hhd\n", mod);
352: if (mod == -1)
353: ret = s_unlck();
354: else
355: ret = s_lck(reason);
356: return ret;
357: }
358:
359: VERB(3) printf("Info(3):: Chroot=%s SUID=%s Mount=%s Timeout=%d Session=%s\n",
360: AIT_GET_STR(&Chroot), AIT_GET_STR(&User),
361: AIT_GET_STR(&Mount), Timeout, szSess);
362:
363: if (!access(szSess, F_OK)) {
364: printf("cfexec already running ...\n");
365: return 127;
366: }
367:
368: if (!argc) {
369: switch (fork()) {
370: case -1:
371: printf("Error:: can`t execute safe mount #%d - %s\n",
372: errno, strerror(errno));
373: return 3;
374: case 0:
375: VERB(5) printf("Info(5):: Go safe mount.\n");
376: setsid();
377:
378: if ((f = mkevent(&chg, &evt)) == -1)
379: return 4;
380:
381: if (update(MNT_UPDATE) == -1) {
382: stat = 5;
383: goto skip;
384: }
385:
386: if (Timeout) {
387: memset(&ts, 0, sizeof ts);
388: ts.tv_sec = Timeout;
389: }
390: switch (kevent(kq, &chg, 1, &evt, 1, !Timeout ? NULL : &ts)) {
391: case -1:
392: printf("Error:: can`t execute safe mount #%d - %s\n",
393: errno, strerror(errno));
394: stat = 7;
395: break;
396: case 0:
397: if (!access(szSLCK, F_OK)) {
398: VERB(1) printf("Timeout reached - service locked\n");
399: break;
400: }
401: VERB(1) printf("Timeout reached - secure mount\n");
402: default:
403: VERB(1) printf("Lock file is deleted - secure mount\n");
404: if (access(szSLCK, F_OK) && update(MNT_UPDATE | MNT_RDONLY) == -1)
405: stat = 8;
406: }
407: skip:
408: close(kq);
409: close(f);
410: unlink(szSess);
411: break;
412: }
413: } else {
414: /*
415: sigemptyset(&sig);
416: sigaddset(&sig, SIGINT);
417: sigaddset(&sig, SIGTSTP);
418: sigprocmask(SIG_BLOCK, &sig, &oldsig);
419: */
420:
421: if ((f = mkevent(NULL, NULL)) == -1)
422: return 4;
423: else
424: close(f);
425:
426: if (update(MNT_UPDATE) == -1) {
427: unlink(szSess);
428: return 5;
429: }
430:
431: switch ((pid = vfork())) {
432: case -1:
433: printf("Error:: can`t execute safe mount #%d - %s\n",
434: errno, strerror(errno));
435: stat = 3;
436: break;
437: case 0:
438: VERB(5) printf("Go to running process %s\n", *argv);
439: if (chroot(AIT_GET_STR(&Chroot)) == -1) {
440: printf("Error:: can`t chroot to dir %s #%d - %s\n",
441: AIT_GET_STR(&Chroot), errno, strerror(errno));
442: } else {
443: if (strncmp(AIT_GET_STR(&User), "root", 5))
444: setuser();
445:
446: /* chdir("/"); */
447: execvp(*argv, argv);
448: }
449: _exit(127);
450: break;
451: default:
452: waitpid(pid, &stat, 0);
453: stat = WEXITSTATUS(stat);
454: VERB(3) printf("Return code: %d\n", stat);
455:
456: if (access(szSLCK, F_OK) && update(MNT_UPDATE | MNT_RDONLY) == -1)
457: stat = 8;
458: break;
459: }
460:
461: unlink(szSess);
462: }
463:
464: return stat;
465: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>