2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <bsm/audit.h>
2N/A#include <bsm/libbsm.h>
2N/A#include <bsm/audit_uevents.h>
2N/A#include <bsm/audit_private.h>
2N/A#include <unistd.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <pwd.h>
2N/A
2N/A#include <locale.h>
2N/A#include "generic.h"
2N/A
2N/A#define AUDIT_GET_DIFFS_NO_CRONTAB 1
2N/A#define AUDIT_GET_DIFFS_CRONTAB 0
2N/A#define AUDIT_GET_DIFFS_ERR -1
2N/A#define AUDIT_GET_DIFFS_NO_DIFFS -2
2N/A
2N/Astatic int audit_crontab_get_diffs(char *cf, char *tmp_name,
2N/A char **bufptr);
2N/A
2N/Aint
2N/Aaudit_crontab_modify(char *path, char *tmp_path, int sorf)
2N/A{
2N/A int r, create = 0;
2N/A char *diffs = NULL;
2N/A
2N/A if (cannot_audit(0)) {
2N/A return (0);
2N/A } else {
2N/A au_event_t event;
2N/A char *anc_name;
2N/A auditinfo_addr_t ai;
2N/A
2N/A if (getaudit_addr(&ai, sizeof (ai))) {
2N/A return (-1);
2N/A }
2N/A
2N/A r = audit_crontab_get_diffs(path, tmp_path, &diffs);
2N/A
2N/A if (r == AUDIT_GET_DIFFS_NO_DIFFS) {
2N/A return (0);
2N/A }
2N/A if (diffs != NULL && r != AUDIT_GET_DIFFS_ERR) {
2N/A aug_save_text(diffs);
2N/A free(diffs);
2N/A }
2N/A
2N/A if (r == AUDIT_GET_DIFFS_NO_CRONTAB) {
2N/A create = 1;
2N/A if (diffs == NULL)
2N/A aug_save_text("");
2N/A }
2N/A
2N/A /*
2N/A * create an ancilary file if audit characteristics exist
2N/A * else delete an ancilary if if one exists
2N/A */
2N/A
2N/A anc_name = audit_cron_make_anc_name(path);
2N/A if (anc_name == NULL)
2N/A r = -1;
2N/A else if (audit_crontab_process_not_audited()) {
2N/A (void) unlink(anc_name);
2N/A free(anc_name);
2N/A } else {
2N/A r = audit_cron_setinfo(anc_name, &ai);
2N/A free(anc_name);
2N/A }
2N/A aug_init();
2N/A aug_save_auid(ai.ai_auid);
2N/A aug_save_euid(geteuid());
2N/A aug_save_egid(getegid());
2N/A aug_save_uid(getuid());
2N/A aug_save_gid(getgid());
2N/A aug_save_pid(getpid());
2N/A aug_save_asid(ai.ai_asid);
2N/A aug_save_tid_ex(ai.ai_termid.at_port, ai.ai_termid.at_addr,
2N/A ai.ai_termid.at_type);
2N/A
2N/A
2N/A aug_save_path(path);
2N/A event = (create) ? AUE_crontab_create : AUE_crontab_mod;
2N/A aug_save_event(event);
2N/A aug_save_sorf(sorf);
2N/A
2N/A if (aug_audit() != 0)
2N/A return (-1);
2N/A return (r);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Aaudit_crontab_delete(char *path, int sorf)
2N/A{
2N/A int r = 0;
2N/A
2N/A if (cannot_audit(0)) {
2N/A return (0);
2N/A } else {
2N/A char *anc_name;
2N/A anc_name = audit_cron_make_anc_name(path);
2N/A if (anc_name != NULL) {
2N/A r = unlink(anc_name);
2N/A free(anc_name);
2N/A } else
2N/A r = -1;
2N/A
2N/A aug_init();
2N/A (void) aug_save_me();
2N/A
2N/A aug_save_path(path);
2N/A aug_save_event(AUE_crontab_delete);
2N/A aug_save_sorf(sorf);
2N/A if (aug_audit() != 0)
2N/A return (-1);
2N/A return (r);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * gets differences between old and new crontab files.
2N/A * arguments:
2N/A * cf - name of crontab file
2N/A * tmp_name - name of new crontab file
2N/A * bufptr - pointer to an array of characters with
2N/A * either an error message or an output of "diff" command.
2N/A *
2N/A * results:
2N/A * AUDIT_GET_DIFFS_ERR - errors;
2N/A * file not exists (do not free *bufptr in this case)
2N/A * AUDIT_GET_DIFFS_NO_DIFFS - errors;
2N/A * file exists (do not free *bufptr in this case)
2N/A * AUDIT_GET_DIFFS_CRONTAB - OK, old crontab file exists.
2N/A * AUDIT_GET_DIFFS_NO_CRONTAB - OK. there is no crontab file.
2N/A */
2N/Astatic int
2N/Aaudit_crontab_get_diffs(char *cf, char *tmp_name, char **bufptr)
2N/A{
2N/A struct stat st, st_tmp;
2N/A uid_t euid;
2N/A int len, r = AUDIT_GET_DIFFS_CRONTAB;
2N/A char *buf = NULL, err_buf[128];
2N/A
2N/A (void) memset(err_buf, 0, 128);
2N/A euid = geteuid();
2N/A if (seteuid(0) == -1) {
2N/A r = AUDIT_GET_DIFFS_ERR;
2N/A (void) snprintf(err_buf, sizeof (err_buf),
2N/A "crontab: seteuid: %s\n", strerror(errno));
2N/A goto exit_diff;
2N/A }
2N/A if (stat(cf, &st) == -1) {
2N/A if (errno == ENOENT) {
2N/A r = AUDIT_GET_DIFFS_NO_CRONTAB;
2N/A } else {
2N/A r = AUDIT_GET_DIFFS_ERR;
2N/A (void) snprintf(err_buf, sizeof (err_buf),
2N/A "crontab: %s: stat: %s\n",
2N/A cf, strerror(errno));
2N/A goto exit_diff;
2N/A }
2N/A len = 0;
2N/A } else
2N/A len = st.st_size;
2N/A
2N/A if (stat(tmp_name, &st_tmp) == -1) {
2N/A r = AUDIT_GET_DIFFS_ERR;
2N/A (void) snprintf(err_buf, sizeof (err_buf),
2N/A "crontab: %s: stat: %s\n",
2N/A tmp_name, strerror(errno));
2N/A goto exit_diff;
2N/A }
2N/A
2N/A if (st_tmp.st_size == 0 && len == 0) {
2N/A /* there is no difference */
2N/A r = AUDIT_GET_DIFFS_NO_DIFFS;
2N/A *bufptr = NULL;
2N/A goto exit_diff;
2N/A }
2N/A
2N/Aexit_diff:
2N/A /* return information on create or update crontab */
2N/A (void) seteuid(euid);
2N/A switch (r) {
2N/A case AUDIT_GET_DIFFS_ERR:
2N/A if (buf != NULL)
2N/A free(buf);
2N/A *bufptr = err_buf;
2N/A break;
2N/A case AUDIT_GET_DIFFS_NO_DIFFS:
2N/A if (buf != NULL)
2N/A free(buf);
2N/A *bufptr = NULL;
2N/A break;
2N/A case AUDIT_GET_DIFFS_CRONTAB:
2N/A if (buf != NULL) {
2N/A if (strlen(buf) != 0) {
2N/A *bufptr = buf;
2N/A } else {
2N/A r = AUDIT_GET_DIFFS_NO_DIFFS;
2N/A *bufptr = NULL;
2N/A }
2N/A }
2N/A break;
2N/A case AUDIT_GET_DIFFS_NO_CRONTAB:
2N/A if (buf != NULL) {
2N/A if (strlen(buf) != 0) {
2N/A *bufptr = buf;
2N/A } else {
2N/A *bufptr = NULL;
2N/A free(buf);
2N/A }
2N/A }
2N/A break;
2N/A }
2N/A
2N/A return (r);
2N/A}
2N/A
2N/A/*
2N/A * audit_crontab_not_allowed determines if we have a case that should be audited
2N/A * but we can't. If auditing is enabled but the current process is not
2N/A * audited, then the ruid of the user doing the editing must be the owner
2N/A * id of the file to be edited.
2N/A *
2N/A * When audit_crontab_not_allowed is called, ruid is for the crontab file
2N/A * to be modified or created.
2N/A */
2N/A
2N/A#define PWD_BUFFER_SIZE 512
2N/A
2N/Aint
2N/Aaudit_crontab_not_allowed(uid_t ruid, char *user) {
2N/A struct passwd pwd;
2N/A char buffer[PWD_BUFFER_SIZE];
2N/A int rc = 0; /* 0 == allow */
2N/A
2N/A if (!cannot_audit(0)) { /* allow access if audit off */
2N/A if (getpwnam_r(user, &pwd, buffer, PWD_BUFFER_SIZE) == NULL) {
2N/A rc = 1; /* deny access if invalid */
2N/A } else if (ruid == pwd.pw_uid)
2N/A rc = 0; /* editing his own crontab */
2N/A else
2N/A rc = audit_crontab_process_not_audited();
2N/A }
2N/A return (rc);
2N/A}
2N/A
2N/Aint
2N/Aaudit_crontab_process_not_audited() {
2N/A struct auditpinfo_addr info;
2N/A int rc;
2N/A
2N/A info.ap_pid = getpid();
2N/A if (auditon(A_GETPINFO_ADDR, (caddr_t)&info, sizeof (info)) != 0)
2N/A rc = 0; /* audit failure: not enabled */
2N/A else
2N/A rc = (info.ap_auid == AU_NOAUDITID);
2N/A
2N/A return (rc);
2N/A}