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 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright (c) 2016 by Delphix. All rights reserved. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/systeminfo.h> 31 #include <bsm/audit.h> 32 #include <bsm/libbsm.h> 33 #include <bsm/audit_uevents.h> 34 #include <bsm/audit_private.h> 35 #include <unistd.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <pwd.h> 39 40 #include <locale.h> 41 #include "generic.h" 42 43 #define AUDIT_GET_DIFFS_NO_CRONTAB 1 44 #define AUDIT_GET_DIFFS_CRONTAB 0 45 #define AUDIT_GET_DIFFS_ERR -1 46 #define AUDIT_GET_DIFFS_NO_DIFFS -2 47 48 static int audit_crontab_get_diffs(char *cf, char *tmp_name, 49 char **bufptr); 50 51 int 52 audit_crontab_modify(char *path, char *tmp_path, int sorf) 53 { 54 int r, create = 0; 55 char *diffs = NULL; 56 57 if (cannot_audit(0)) { 58 return (0); 59 } else { 60 au_event_t event; 61 char *anc_name; 62 auditinfo_addr_t ai; 63 64 if (getaudit_addr(&ai, sizeof (ai))) { 65 return (-1); 66 } 67 68 r = audit_crontab_get_diffs(path, tmp_path, &diffs); 69 70 if (r == AUDIT_GET_DIFFS_NO_DIFFS) { 71 return (0); 72 } 73 if (diffs != NULL && r != AUDIT_GET_DIFFS_ERR) { 74 aug_save_text(diffs); 75 free(diffs); 76 } 77 78 if (r == AUDIT_GET_DIFFS_NO_CRONTAB) { 79 create = 1; 80 if (diffs == NULL) 81 aug_save_text(""); 82 } 83 84 /* 85 * create an ancilary file if audit characteristics exist 86 * else delete an ancilary if if one exists 87 */ 88 89 anc_name = audit_cron_make_anc_name(path); 90 if (anc_name == NULL) 91 r = -1; 92 else if (audit_crontab_process_not_audited()) { 93 (void) unlink(anc_name); 94 free(anc_name); 95 } else { 96 r = audit_cron_setinfo(anc_name, &ai); 97 free(anc_name); 98 } 99 aug_init(); 100 aug_save_auid(ai.ai_auid); 101 aug_save_euid(geteuid()); 102 aug_save_egid(getegid()); 103 aug_save_uid(getuid()); 104 aug_save_gid(getgid()); 105 aug_save_pid(getpid()); 106 aug_save_asid(ai.ai_asid); 107 aug_save_tid_ex(ai.ai_termid.at_port, ai.ai_termid.at_addr, 108 ai.ai_termid.at_type); 109 110 111 aug_save_path(path); 112 event = (create) ? AUE_crontab_create : AUE_crontab_mod; 113 aug_save_event(event); 114 aug_save_sorf(sorf); 115 116 if (aug_audit() != 0) 117 return (-1); 118 return (r); 119 } 120 } 121 122 int 123 audit_crontab_delete(char *path, int sorf) 124 { 125 int r = 0; 126 127 if (cannot_audit(0)) { 128 return (0); 129 } else { 130 char *anc_name; 131 anc_name = audit_cron_make_anc_name(path); 132 if (anc_name != NULL) { 133 r = unlink(anc_name); 134 free(anc_name); 135 } else 136 r = -1; 137 138 aug_init(); 139 (void) aug_save_me(); 140 141 aug_save_path(path); 142 aug_save_event(AUE_crontab_delete); 143 aug_save_sorf(sorf); 144 if (aug_audit() != 0) 145 return (-1); 146 return (r); 147 } 148 } 149 150 /* 151 * gets differences between old and new crontab files. 152 * arguments: 153 * cf - name of crontab file 154 * tmp_name - name of new crontab file 155 * bufptr - pointer to an array of characters with 156 * either an error message or an output of "diff" command. 157 * 158 * results: 159 * AUDIT_GET_DIFFS_ERR - errors; 160 * file not exists (do not free *bufptr in this case) 161 * AUDIT_GET_DIFFS_NO_DIFFS - errors; 162 * file exists (do not free *bufptr in this case) 163 * AUDIT_GET_DIFFS_CRONTAB - OK, old crontab file exists. 164 * AUDIT_GET_DIFFS_NO_CRONTAB - OK. there is no crontab file. 165 */ 166 static int 167 audit_crontab_get_diffs(char *cf, char *tmp_name, char **bufptr) 168 { 169 struct stat st, st_tmp; 170 uid_t euid; 171 int len, r = AUDIT_GET_DIFFS_CRONTAB; 172 char *buf = NULL, err_buf[128]; 173 174 (void) memset(err_buf, 0, 128); 175 euid = geteuid(); 176 if (seteuid(0) == -1) { 177 r = AUDIT_GET_DIFFS_ERR; 178 (void) snprintf(err_buf, sizeof (err_buf), 179 "crontab: seteuid: %s\n", strerror(errno)); 180 goto exit_diff; 181 } 182 if (stat(cf, &st) == -1) { 183 if (errno == ENOENT) { 184 r = AUDIT_GET_DIFFS_NO_CRONTAB; 185 } else { 186 r = AUDIT_GET_DIFFS_ERR; 187 (void) snprintf(err_buf, sizeof (err_buf), 188 "crontab: %s: stat: %s\n", 189 cf, strerror(errno)); 190 goto exit_diff; 191 } 192 len = 0; 193 } else 194 len = st.st_size; 195 196 if (stat(tmp_name, &st_tmp) == -1) { 197 r = AUDIT_GET_DIFFS_ERR; 198 (void) snprintf(err_buf, sizeof (err_buf), 199 "crontab: %s: stat: %s\n", 200 tmp_name, strerror(errno)); 201 goto exit_diff; 202 } 203 204 if (st_tmp.st_size == 0 && len == 0) { 205 /* there is no difference */ 206 r = AUDIT_GET_DIFFS_NO_DIFFS; 207 *bufptr = NULL; 208 goto exit_diff; 209 } 210 211 exit_diff: 212 /* return information on create or update crontab */ 213 (void) seteuid(euid); 214 switch (r) { 215 case AUDIT_GET_DIFFS_ERR: 216 if (buf != NULL) 217 free(buf); 218 *bufptr = err_buf; 219 break; 220 case AUDIT_GET_DIFFS_NO_DIFFS: 221 if (buf != NULL) 222 free(buf); 223 *bufptr = NULL; 224 break; 225 case AUDIT_GET_DIFFS_CRONTAB: 226 if (buf != NULL) { 227 if (strlen(buf) != 0) { 228 *bufptr = buf; 229 } else { 230 r = AUDIT_GET_DIFFS_NO_DIFFS; 231 *bufptr = NULL; 232 } 233 } 234 break; 235 case AUDIT_GET_DIFFS_NO_CRONTAB: 236 if (buf != NULL) { 237 if (strlen(buf) != 0) { 238 *bufptr = buf; 239 } else { 240 *bufptr = NULL; 241 free(buf); 242 } 243 } 244 break; 245 } 246 247 return (r); 248 } 249 250 /* 251 * audit_crontab_not_allowed determines if we have a case that should be audited 252 * but we can't. If auditing is enabled but the current process is not 253 * audited, then the ruid of the user doing the editing must be the owner 254 * id of the file to be edited. 255 * 256 * When audit_crontab_not_allowed is called, ruid is for the crontab file 257 * to be modified or created. 258 */ 259 260 #define PWD_BUFFER_SIZE 512 261 262 int 263 audit_crontab_not_allowed(uid_t ruid, char *user) { 264 struct passwd pwd; 265 char buffer[PWD_BUFFER_SIZE]; 266 int rc = 0; /* 0 == allow */ 267 268 if (!cannot_audit(0)) { /* allow access if audit off */ 269 if (getpwnam_r(user, &pwd, buffer, PWD_BUFFER_SIZE) == NULL) { 270 rc = 1; /* deny access if invalid */ 271 } else if (ruid == pwd.pw_uid) 272 rc = 0; /* editing their own crontab */ 273 else 274 rc = audit_crontab_process_not_audited(); 275 } 276 return (rc); 277 } 278 279 int 280 audit_crontab_process_not_audited() { 281 struct auditpinfo_addr info; 282 int rc; 283 284 info.ap_pid = getpid(); 285 if (auditon(A_GETPINFO_ADDR, (caddr_t)&info, sizeof (info)) != 0) 286 rc = 0; /* audit failure: not enabled */ 287 else 288 rc = (info.ap_auid == AU_NOAUDITID); 289 290 return (rc); 291 } 292