1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <sys/systeminfo.h>
28 #include <bsm/audit.h>
29 #include <bsm/libbsm.h>
30 #include <bsm/audit_uevents.h>
31 #include <bsm/audit_private.h>
32 #include <unistd.h>
33 #include <wait.h>
34 #include <fcntl.h>
35 #include <pwd.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <errno.h>
39 #include <syslog.h>
40 #include <sys/stat.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <libgen.h>
45
46 #include <locale.h>
47 #include "generic.h"
48
49 #define F_AUID "%u\n"
50 #define F_SMASK "%x\n"
51 #define F_FMASK "%x\n"
52 #define F_PORT "%lx\n"
53 #define F_TYPE "%x\n"
54 #define F_MACH "%x %x %x %x\n"
55 #define F_ASID "%u\n"
56
57 #define AU_SUFFIX ".au"
58
59 #define ANC_BAD_FILE -1
60 #define ANC_BAD_FORMAT -2
61
62 #define AUDIT_CRON_TEXTBUF 256
63 static char textbuf[AUDIT_CRON_TEXTBUF];
64
65 int
audit_cron_mode()66 audit_cron_mode()
67 {
68 return (!cannot_audit(0));
69 }
70
71 static void
audit_cron_syslog(const char * message)72 audit_cron_syslog(const char *message) {
73 static int is_open = 0;
74
75 if (!is_open) {
76 openlog("Solaris_Audit", LOG_ODELAY, LOG_CRON);
77 is_open = 1;
78 }
79 syslog(LOG_WARNING, "%s", message);
80 }
81
82 /*
83 * audit_cron_getinfo returns the audit characteristics from the relevant
84 * auxiliary file, it if exists. If not, it creates them from the crontab
85 * or atjob uid.
86 */
87
88 static int
audit_cron_getinfo(char * fname,char * fname_aux,struct auditinfo_addr * info)89 audit_cron_getinfo(char *fname, char *fname_aux, struct auditinfo_addr *info)
90 {
91 int fd;
92 struct stat st;
93 au_mask_t mask;
94 struct passwd pwd;
95 char pwd_buff[1024];
96 static char *msg =
97 "Used defaults instead of ancilary audit file";
98
99 if ((fd = open(fname_aux, O_RDONLY)) == -1) {
100 /* no syslog here; common case */
101 goto make_it_up;
102 }
103 if (fstat(fd, &st) == -1) {
104 /* no syslog here either; common case */
105 goto delete_first;
106 }
107
108 if (read(fd, textbuf, st.st_size) != st.st_size) {
109 audit_cron_syslog(msg);
110 goto delete_first;
111 }
112
113 if (sscanf(textbuf,
114 F_AUID
115 F_SMASK
116 F_FMASK
117 F_PORT
118 F_TYPE
119 F_MACH
120 F_ASID,
121 &(info->ai_auid),
122 &(info->ai_mask.am_success),
123 &(info->ai_mask.am_failure),
124 &(info->ai_termid.at_port),
125 &(info->ai_termid.at_type),
126 &(info->ai_termid.at_addr[0]),
127 &(info->ai_termid.at_addr[1]),
128 &(info->ai_termid.at_addr[2]),
129 &(info->ai_termid.at_addr[3]),
130 &(info->ai_asid)) != 10) {
131 audit_cron_syslog(msg);
132 goto delete_first;
133 }
134 (void) close(fd);
135 return (0);
136
137 delete_first:
138 (void) close(fd);
139 if (unlink(fname_aux)) {
140 if (errno != ENOENT)
141 audit_cron_syslog(
142 "Failed to remove invalid ancilary audit file");
143 }
144 /* intentionally falls through */
145
146 make_it_up:
147 if (stat(fname, &st))
148 return (-1);
149
150 /* port and IP are zero */
151 (void) memset(&(info->ai_termid), 0, sizeof (au_tid_addr_t));
152 info->ai_termid.at_type = AU_IPv4;
153
154 /* the caller is the child of cron which will run the job. */
155 info->ai_asid = getpid();
156
157 info->ai_mask.am_success = 0; /* cover error case */
158 info->ai_mask.am_failure = 0;
159
160 if (strstr(fname, "crontabs") != NULL) {
161 if (getpwnam_r(basename(fname), &pwd, pwd_buff,
162 sizeof (pwd_buff)) == NULL)
163 return (-1); /* getpwnam_r sets errno */
164 } else {
165 if (getpwuid_r(st.st_uid, &pwd, pwd_buff, sizeof (pwd_buff)) ==
166 NULL)
167 return (-1); /* getpwuid_r sets errno */
168 }
169
170 info->ai_auid = pwd.pw_uid;
171
172 if (au_user_mask(pwd.pw_name, &mask)) {
173 errno = EINVAL; /* pw_name lookup failed */
174 return (-1);
175 }
176 info->ai_mask.am_success = mask.am_success;
177 info->ai_mask.am_failure = mask.am_failure;
178
179 return (0);
180 }
181
182 int
audit_cron_setinfo(char * fname,struct auditinfo_addr * info)183 audit_cron_setinfo(char *fname, struct auditinfo_addr *info)
184 {
185 int fd, len, r;
186 int save_err;
187
188 r = chmod(fname, 0200);
189 if (r == -1 && errno != ENOENT)
190 return (-1);
191
192 if ((fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, 0200)) == -1)
193 return (-1);
194
195 len = sprintf(textbuf,
196 F_AUID
197 F_SMASK
198 F_FMASK
199 F_PORT
200 F_TYPE
201 F_MACH
202 F_ASID,
203 info->ai_auid,
204 info->ai_mask.am_success,
205 info->ai_mask.am_failure,
206 info->ai_termid.at_port,
207 info->ai_termid.at_type,
208 info->ai_termid.at_addr[0],
209 info->ai_termid.at_addr[1],
210 info->ai_termid.at_addr[2],
211 info->ai_termid.at_addr[3],
212 info->ai_asid);
213
214 if (write(fd, textbuf, len) != len)
215 goto audit_setinfo_clean;
216
217 if (fchmod(fd, 0400) == -1)
218 goto audit_setinfo_clean;
219
220 (void) close(fd);
221 return (0);
222
223 audit_setinfo_clean:
224 save_err = errno;
225 (void) close(fd);
226 (void) unlink(fname);
227 errno = save_err;
228 return (-1);
229 }
230
231 char *
audit_cron_make_anc_name(char * fname)232 audit_cron_make_anc_name(char *fname)
233 {
234 char *anc_name;
235
236 anc_name = (char *)malloc(strlen(fname) + strlen(AU_SUFFIX) + 1);
237 if (anc_name == NULL)
238 return (NULL);
239
240 (void) strcpy(anc_name, fname);
241 (void) strcat(anc_name, AU_SUFFIX);
242 return (anc_name);
243 }
244
245 int
audit_cron_is_anc_name(char * name)246 audit_cron_is_anc_name(char *name)
247 {
248 int pos;
249
250 pos = strlen(name) - strlen(AU_SUFFIX);
251 if (pos <= 0)
252 return (0);
253
254 if (strcmp(name + pos, AU_SUFFIX) == 0)
255 return (1);
256
257 return (0);
258 }
259
260 static void
audit_cron_session_failure(char * name,int type,char * err_str)261 audit_cron_session_failure(char *name, int type, char *err_str)
262 {
263 const char *mess;
264
265 if (type == 0)
266 mess = dgettext(bsm_dom,
267 "at-job session for user %s failed: ancillary file: %s");
268 else
269 mess = dgettext(bsm_dom,
270 "crontab job session for user %s failed: ancillary file: %s");
271
272 (void) snprintf(textbuf, sizeof (textbuf), mess, name, err_str);
273
274 aug_save_event(AUE_cron_invoke);
275 aug_save_sorf(4);
276 aug_save_text(textbuf);
277 (void) aug_audit();
278 }
279
280
281 int
audit_cron_session(char * name,char * path,uid_t uid,gid_t gid,char * at_jobname)282 audit_cron_session(
283 char *name,
284 char *path,
285 uid_t uid,
286 gid_t gid,
287 char *at_jobname)
288 {
289 struct auditinfo_addr info;
290 au_mask_t mask;
291 char *anc_file, *fname;
292 int r = 0;
293 char full_path[PATH_MAX];
294
295 if (cannot_audit(0)) {
296 return (0);
297 }
298
299 /* get auditinfo from ancillary file */
300 if (at_jobname == NULL) {
301 /*
302 * this is a cron-event, so we can get
303 * filename from "name" arg
304 */
305 fname = name;
306 if (path != NULL) {
307 if (strlen(path) + strlen(fname) + 2 > PATH_MAX) {
308 errno = ENAMETOOLONG;
309 r = -1;
310 }
311 (void) strcat(strcat(strcpy(full_path, path), "/"),
312 fname);
313 fname = full_path;
314 }
315 } else {
316 /* this is an at-event, use "at_jobname" */
317 fname = at_jobname;
318 }
319
320 if (r == 0) {
321 anc_file = audit_cron_make_anc_name(fname);
322 if (anc_file == NULL) {
323 r = -1;
324 } else {
325 r = audit_cron_getinfo(fname, anc_file, &info);
326 }
327 }
328
329 if (r != 0) {
330 char *err_str;
331
332 if (r == ANC_BAD_FORMAT)
333 err_str = dgettext(bsm_dom, "bad format");
334 else
335 err_str = strerror(errno);
336
337 audit_cron_session_failure(name,
338 at_jobname == NULL,
339 err_str);
340 if (anc_file != NULL)
341 free(anc_file);
342 return (r);
343 }
344
345 free(anc_file);
346 aug_init();
347
348 /* get current audit masks */
349 if (au_user_mask(name, &mask) == 0) {
350 info.ai_mask.am_success |= mask.am_success;
351 info.ai_mask.am_failure |= mask.am_failure;
352 }
353
354 /* save audit attributes for further use in current process */
355 aug_save_auid(info.ai_auid);
356 aug_save_asid(info.ai_asid);
357 aug_save_tid_ex(info.ai_termid.at_port, info.ai_termid.at_addr,
358 info.ai_termid.at_type);
359 aug_save_pid(getpid());
360 aug_save_uid(uid);
361 aug_save_gid(gid);
362 aug_save_euid(uid);
363 aug_save_egid(gid);
364
365 /* set mixed audit masks */
366 return (setaudit_addr(&info, sizeof (info)));
367 }
368
369 /*
370 * audit_cron_new_job - create audit record with an information
371 * about new job started by cron.
372 * args:
373 * cmd - command being run by cron daemon.
374 * type - type of job (0 - at-job, 1 - crontab job).
375 * event - not used. pointer to cron event structure.
376 */
377 /*ARGSUSED*/
378 void
audit_cron_new_job(char * cmd,int type,void * event)379 audit_cron_new_job(char *cmd, int type, void *event)
380 {
381 if (cannot_audit(0))
382 return;
383
384 if (type == 0) {
385 (void) snprintf(textbuf, sizeof (textbuf),
386 dgettext(bsm_dom, "at-job"));
387 } else if (type == 1) {
388 (void) snprintf(textbuf, sizeof (textbuf),
389 dgettext(bsm_dom, "batch-job"));
390 } else if (type == 2) {
391 (void) snprintf(textbuf, sizeof (textbuf),
392 dgettext(bsm_dom, "crontab-job"));
393 } else if ((type > 2) && (type <= 25)) { /* 25 from cron.h */
394 (void) snprintf(textbuf, sizeof (textbuf),
395 dgettext(bsm_dom, "queue-job (%c)"), (type+'a'));
396 } else {
397 (void) snprintf(textbuf, sizeof (textbuf),
398 dgettext(bsm_dom, "unknown job type (%d)"), type);
399 }
400
401 aug_save_event(AUE_cron_invoke);
402 aug_save_sorf(0);
403 aug_save_text(textbuf);
404 aug_save_text1(cmd);
405 (void) aug_audit();
406 }
407
408 void
audit_cron_bad_user(char * name)409 audit_cron_bad_user(char *name)
410 {
411 if (cannot_audit(0))
412 return;
413
414 (void) snprintf(textbuf, sizeof (textbuf),
415 dgettext(bsm_dom, "bad user %s"), name);
416
417 aug_save_event(AUE_cron_invoke);
418 aug_save_sorf(2);
419 aug_save_text(textbuf);
420 (void) aug_audit();
421 }
422
423 void
audit_cron_user_acct_expired(char * name)424 audit_cron_user_acct_expired(char *name)
425 {
426 if (cannot_audit(0))
427 return;
428
429 (void) snprintf(textbuf, sizeof (textbuf),
430 dgettext(bsm_dom,
431 "user %s account expired"), name);
432
433 aug_save_event(AUE_cron_invoke);
434 aug_save_sorf(3);
435 aug_save_text(textbuf);
436 (void) aug_audit();
437 }
438
439 int
audit_cron_create_anc_file(char * name,char * path,char * uname,uid_t uid)440 audit_cron_create_anc_file(char *name, char *path, char *uname, uid_t uid)
441 {
442 au_mask_t msk;
443 auditinfo_addr_t ai;
444 int pid;
445 char *anc_name;
446 char full_path[PATH_MAX];
447
448 if (cannot_audit(0))
449 return (0);
450
451 if (name == NULL)
452 return (0);
453
454 if (path != NULL) {
455 if (strlen(path) + strlen(name) + 2 > PATH_MAX)
456 return (-1);
457 (void) strcat(strcat(strcpy(full_path, path), "/"), name);
458 name = full_path;
459 }
460 anc_name = audit_cron_make_anc_name(name);
461
462 if (access(anc_name, F_OK) != 0) {
463 if (au_user_mask(uname, &msk) != 0) {
464 free(anc_name);
465 return (-1);
466 }
467
468 ai.ai_mask = msk;
469 ai.ai_auid = uid;
470 ai.ai_termid.at_port = 0;
471 ai.ai_termid.at_type = AU_IPv4;
472 ai.ai_termid.at_addr[0] = 0;
473 ai.ai_termid.at_addr[1] = 0;
474 ai.ai_termid.at_addr[2] = 0;
475 ai.ai_termid.at_addr[3] = 0;
476 /* generate new pid to use it as asid */
477 pid = vfork();
478 if (pid == -1) {
479 free(anc_name);
480 return (-1);
481 }
482 if (pid == 0)
483 exit(0);
484 else {
485 /*
486 * we need to clear status of children for
487 * wait() call in "cron"
488 */
489 int lock;
490
491 (void) waitpid(pid, &lock, 0);
492 }
493 ai.ai_asid = pid;
494 if (audit_cron_setinfo(anc_name, &ai) != 0) {
495 free(anc_name);
496 return (-1);
497 }
498 }
499
500 free(anc_name);
501 return (0);
502 }
503
504 int
audit_cron_delete_anc_file(char * name,char * path)505 audit_cron_delete_anc_file(char *name, char *path)
506 {
507 char *anc_name;
508 char full_path[PATH_MAX];
509 int r;
510
511 if (name == NULL)
512 return (0);
513
514 if (path != NULL) {
515 if (strlen(path) + strlen(name) + 2 > PATH_MAX)
516 return (-1);
517 (void) strcat(strcat(strcpy(full_path, path), "/"), name);
518 name = full_path;
519 }
520 anc_name = audit_cron_make_anc_name(name);
521 r = unlink(anc_name);
522 free(anc_name);
523 return (r);
524 }
525