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