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; 173 static char err_buf[128]; 174 175 (void) memset(err_buf, 0, 128); 176 euid = geteuid(); 177 if (seteuid(0) == -1) { 178 r = AUDIT_GET_DIFFS_ERR; 179 (void) snprintf(err_buf, sizeof (err_buf), 180 "crontab: seteuid: %s\n", strerror(errno)); 181 goto exit_diff; 182 } 183 if (stat(cf, &st) == -1) { 184 if (errno == ENOENT) { 185 r = AUDIT_GET_DIFFS_NO_CRONTAB; 186 } else { 187 r = AUDIT_GET_DIFFS_ERR; 188 (void) snprintf(err_buf, sizeof (err_buf), 189 "crontab: %s: stat: %s\n", 190 cf, strerror(errno)); 191 goto exit_diff; 192 } 193 len = 0; 194 } else 195 len = st.st_size; 196 197 if (stat(tmp_name, &st_tmp) == -1) { 198 r = AUDIT_GET_DIFFS_ERR; 199 (void) snprintf(err_buf, sizeof (err_buf), 200 "crontab: %s: stat: %s\n", 201 tmp_name, strerror(errno)); 202 goto exit_diff; 203 } 204 205 if (st_tmp.st_size == 0 && len == 0) { 206 /* there is no difference */ 207 r = AUDIT_GET_DIFFS_NO_DIFFS; 208 *bufptr = NULL; 209 goto exit_diff; 210 } 211 212 exit_diff: 213 /* return information on create or update crontab */ 214 (void) seteuid(euid); 215 switch (r) { 216 case AUDIT_GET_DIFFS_ERR: 217 if (buf != NULL) 218 free(buf); 219 *bufptr = err_buf; 220 break; 221 case AUDIT_GET_DIFFS_NO_DIFFS: 222 if (buf != NULL) 223 free(buf); 224 *bufptr = NULL; 225 break; 226 case AUDIT_GET_DIFFS_CRONTAB: 227 if (buf != NULL) { 228 if (strlen(buf) != 0) { 229 *bufptr = buf; 230 } else { 231 r = AUDIT_GET_DIFFS_NO_DIFFS; 232 *bufptr = NULL; 233 } 234 } 235 break; 236 case AUDIT_GET_DIFFS_NO_CRONTAB: 237 if (buf != NULL) { 238 if (strlen(buf) != 0) { 239 *bufptr = buf; 240 } else { 241 *bufptr = NULL; 242 free(buf); 243 } 244 } 245 break; 246 } 247 248 return (r); 249 } 250 251 /* 252 * audit_crontab_not_allowed determines if we have a case that should be audited 253 * but we can't. If auditing is enabled but the current process is not 254 * audited, then the ruid of the user doing the editing must be the owner 255 * id of the file to be edited. 256 * 257 * When audit_crontab_not_allowed is called, ruid is for the crontab file 258 * to be modified or created. 259 */ 260 261 #define PWD_BUFFER_SIZE 512 262 263 int 264 audit_crontab_not_allowed(uid_t ruid, char *user) 265 { 266 struct passwd pwd; 267 char buffer[PWD_BUFFER_SIZE]; 268 int rc = 0; /* 0 == allow */ 269 270 if (!cannot_audit(0)) { /* allow access if audit off */ 271 if (getpwnam_r(user, &pwd, buffer, PWD_BUFFER_SIZE) == NULL) { 272 rc = 1; /* deny access if invalid */ 273 } else if (ruid == pwd.pw_uid) 274 rc = 0; /* editing their own crontab */ 275 else 276 rc = audit_crontab_process_not_audited(); 277 } 278 return (rc); 279 } 280 281 int 282 audit_crontab_process_not_audited() 283 { 284 struct auditpinfo_addr info; 285 int rc; 286 287 info.ap_pid = getpid(); 288 if (auditon(A_GETPINFO_ADDR, (caddr_t)&info, sizeof (info)) != 0) 289 rc = 0; /* audit failure: not enabled */ 290 else 291 rc = (info.ap_auid == AU_NOAUDITID); 292 293 return (rc); 294 } 295