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