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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <sys/systeminfo.h> 32 #include <bsm/audit.h> 33 #include <bsm/libbsm.h> 34 #include <bsm/audit_uevents.h> 35 #include <bsm/audit_private.h> 36 #include <unistd.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <pwd.h> 40 41 #include <locale.h> 42 #include "generic.h" 43 44 #define AUDIT_GET_DIFFS_NO_CRONTAB 1 45 #define AUDIT_GET_DIFFS_CRONTAB 0 46 #define AUDIT_GET_DIFFS_ERR -1 47 #define AUDIT_GET_DIFFS_NO_DIFFS -2 48 49 static int audit_crontab_get_diffs(char *cf, char *tmp_name, 50 char **bufptr); 51 52 int 53 audit_crontab_modify(char *path, char *tmp_path, int sorf) 54 { 55 int r, create = 0; 56 char *diffs = NULL; 57 58 if (cannot_audit(0)) { 59 return (0); 60 } else { 61 au_event_t event; 62 char *anc_name; 63 auditinfo_addr_t ai; 64 65 if (getaudit_addr(&ai, sizeof (ai))) { 66 return (-1); 67 } 68 69 r = audit_crontab_get_diffs(path, tmp_path, &diffs); 70 71 if (r == AUDIT_GET_DIFFS_NO_DIFFS) { 72 return (0); 73 } 74 if (diffs != NULL && r != AUDIT_GET_DIFFS_ERR) { 75 aug_save_text(diffs); 76 free(diffs); 77 } 78 79 if (r == AUDIT_GET_DIFFS_NO_CRONTAB) { 80 create = 1; 81 if (diffs == NULL) 82 aug_save_text(""); 83 } 84 85 /* 86 * create an ancilary file if audit characteristics exist 87 * else delete an ancilary if if one exists 88 */ 89 90 anc_name = audit_cron_make_anc_name(path); 91 if (anc_name == NULL) 92 r = -1; 93 else if (audit_crontab_process_not_audited()) { 94 (void) unlink(anc_name); 95 free(anc_name); 96 } else { 97 r = audit_cron_setinfo(anc_name, &ai); 98 free(anc_name); 99 } 100 aug_init(); 101 aug_save_auid(ai.ai_auid); 102 aug_save_euid(geteuid()); 103 aug_save_egid(getegid()); 104 aug_save_uid(getuid()); 105 aug_save_gid(getgid()); 106 aug_save_pid(getpid()); 107 aug_save_asid(ai.ai_asid); 108 aug_save_tid_ex(ai.ai_termid.at_port, ai.ai_termid.at_addr, 109 ai.ai_termid.at_type); 110 111 112 aug_save_path(path); 113 event = (create) ? AUE_crontab_create : AUE_crontab_mod; 114 aug_save_event(event); 115 aug_save_sorf(sorf); 116 117 if (aug_audit() != 0) 118 return (-1); 119 return (r); 120 } 121 } 122 123 int 124 audit_crontab_delete(char *path, int sorf) 125 { 126 int r = 0; 127 128 if (cannot_audit(0)) { 129 return (0); 130 } else { 131 char *anc_name; 132 anc_name = audit_cron_make_anc_name(path); 133 if (anc_name != NULL) { 134 r = unlink(anc_name); 135 free(anc_name); 136 } else 137 r = -1; 138 139 aug_init(); 140 (void) aug_save_me(); 141 142 aug_save_path(path); 143 aug_save_event(AUE_crontab_delete); 144 aug_save_sorf(sorf); 145 if (aug_audit() != 0) 146 return (-1); 147 return (r); 148 } 149 } 150 151 /* 152 * gets differences between old and new crontab files. 153 * arguments: 154 * cf - name of crontab file 155 * tmp_name - name of new crontab file 156 * bufptr - pointer to an array of characters with 157 * either an error message or an output of "diff" command. 158 * 159 * results: 160 * AUDIT_GET_DIFFS_ERR - errors; 161 * file not exists (do not free *bufptr in this case) 162 * AUDIT_GET_DIFFS_NO_DIFFS - errors; 163 * file exists (do not free *bufptr in this case) 164 * AUDIT_GET_DIFFS_CRONTAB - OK, old crontab file exists. 165 * AUDIT_GET_DIFFS_NO_CRONTAB - OK. there is no crontab file. 166 */ 167 static int 168 audit_crontab_get_diffs(char *cf, char *tmp_name, char **bufptr) 169 { 170 struct stat st, st_tmp; 171 uid_t euid; 172 int len, r = AUDIT_GET_DIFFS_CRONTAB; 173 char *buf = NULL, 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 struct passwd pwd; 266 char buffer[PWD_BUFFER_SIZE]; 267 int rc = 0; /* 0 == allow */ 268 269 if (!cannot_audit(0)) { /* allow access if audit off */ 270 if (getpwnam_r(user, &pwd, buffer, PWD_BUFFER_SIZE) == NULL) { 271 rc = 1; /* deny access if invalid */ 272 } else if (ruid == pwd.pw_uid) 273 rc = 0; /* editing their own crontab */ 274 else 275 rc = audit_crontab_process_not_audited(); 276 } 277 return (rc); 278 } 279 280 int 281 audit_crontab_process_not_audited() { 282 struct auditpinfo_addr info; 283 int rc; 284 285 info.ap_pid = getpid(); 286 if (auditon(A_GETPINFO_ADDR, (caddr_t)&info, sizeof (info)) != 0) 287 rc = 0; /* audit failure: not enabled */ 288 else 289 rc = (info.ap_auid == AU_NOAUDITID); 290 291 return (rc); 292 } 293