Archive for August, 2008

Final results

Saturday, August 16th, 2008

Hi all, it has been a long time since my last post. Due to some problems I didn’t have the chance to update this blog. Anyhow before the end of the soc, I’m here to speak about what I did in this period.First of all my auditpipe patch changed a bit: it supports only pid-based subscription to events due to internal decision taken by me and other developers. How to use it then?
fd = open(“/dev/auditpipe”, O_RDONLY);
if (fd < 0)
err(-1, “/dev/auditpipe”);

entry = malloc(sizeof(struct auditpipe_ioctl_preselect_proc));
if(entry == NULL)
err(-1, “MALLOC”);

value = AUDITPIPE_PRESELECT_MODE_PROC;
if (ioctl(fd, AUDITPIPE_SET_PRESELECT_MODE, &value) aipp_pid = pid;

if (ioctl(fd, AUDITPIPE_SET_PRESELECT_PROC, entry) < 0)
err(-1, “AUDITPIPE_SET_PRESELECT_EVENTS”);

What it does is to open auditpipe, change the preselection mode to AUDITPIPE_PRESELECT_MODE_PROC and set up an entry with a specific pid. From there on auditpipe will trace that specific pid. Clean and concise:P   Now the second half of the project. I ought to write a regression test framework. After several unsuccessful attempts, I figured out how to make it works with a bit of shared memory. The general idea is to have a testing function/process which communicates with a parent process and exchange runtime information in order to run the test.  The testing function will take read records from auditpipe and compare them with runtime information collected before. If some inconsistencies are found they are reported. More in details, here is a sample testing module:
/*-
* Copyright (c) 2008 Vincenzo Iozzo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS “AS IS” AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/

#include “audit_pipe_regression_test_utils.h”

struct sysctl_record
{
int mib[6];
char err_val[256];
int ret;
int index;
struct sysctl_record *next;
};

struct sysctl_record *
add_field(struct sysctl_record *head, struct sysctl_record *new)
{
struct sysctl_record *tmp;

tmp = head;
if(tmp == NULL) {
new->next = NULL;
return (new);
}

/* Reach the last element of the list*/
for(; tmp->next != NULL;tmp = tmp->next);

new->index = tmp->index +1;
tmp->next= new;
new->next = NULL;

return (head);
}

void test_sysctl()
{
int mib[2];
int ret, val;

init_channel_primary();

mib[0] = CTL_KERN;
mib[1] = KERN_SECURELVL;
val = 2;
ret = sysctl(&mib, 2, NULL, NULL, &val, sizeof(int));

write_int(mib[0], “arg”);
write_int(mib[1], “arg1″);
write_int(ret, “ret”);
if (ret == -1)
write_string(strerror(errno), “ret_val”);
else
write_string(“success”, “ret_val”);
write_end();

val = 1;
ret = sysctl(&mib, 2, NULL, NULL, &val, sizeof(int));
write_int(mib[0], “arg”);
write_int(mib[1], “arg1″);
write_int(ret, “ret”);
if (ret == -1)
write_string(strerror(errno), “ret_val”);
else
write_string(“success”, “ret_val”);
write_end();

end_channel();

/* Let auditsysctl flush all data */
sleep(5);
return;
}

void validate(FILE *f, struct sysctl_record *head, char *path, pid_t pid)
{
int i, ret, reclen, bytesread, to_parse, arg_counter;
struct sysctl_record *tmp, *elem;
int err = -1;
tokenstr_t tok;
u_char *buf;
u_char type = 0;
FILE *fp;
long control_flag;

to_parse = WAITING;
arg_counter = 0;
control_flag = 0;

fp = fopen(path, “r”);
if(fp == NULL)
return;

tmp = head;
if(tmp == NULL)
return;

while(1) {

to_parse = WAITING;
if(!err || reclen == -1)
break;

/* Record must begin with a header token. */
do {
type = fgetc(fp);
} while(type != AU_HEADER_32_TOKEN);
ungetc(type, fp);

while ((reclen = au_read_rec(fp, &buf)) != -1) {
bytesread = 0;
while (bytesread mib[0]);
else
ret = check_arg(tok.tt.arg32, tmp->mib[1]);

if(!ret)
report_error(tok, f);
break;

case AUT_RETURN32:
TOKEN_FLAG_SET(control_flag, TOKEN_RETURN);
ret = check_ret(tok.tt.ret32, tmp->ret, tmp->err_val);
if(!ret)
report_error(tok, f);
break;

case AUT_TRAILER:
to_parse = WAITING;
if(!TOKEN_FLAG_ISSET(control_flag, TOKEN_SUBJECT))
report_error_string(“Missing Subject token”, f);
if (!TOKEN_FLAG_ISSET(control_flag, TOKEN_ARG) || arg_counter != 2)
report_error_string(“Missing Argument token”, f);
if(!TOKEN_FLAG_ISSET(control_flag, TOKEN_RETURN))
report_error_string(“Missing Return token”, f);
control_flag = 0;
arg_counter = 0;
if(tmp->next != NULL)
tmp = tmp->next;
break;

default:
break;
}
}
}
}
}

free(buf);
fclose(fp);
}

int
main(int argc, char *argv[])
{
int i, quit, fd, value, index, fdout, count;
char *shared_string, *descr, path[512];
pid_t pid;
struct sysctl_record *head, *elem;
FILE *f;

index = 0;
head = NULL;

init_channel();

pid = fork();
if(!pid) {
sleep(15);
test_sysctl();
} else {
fd = setup_auditpipe(pid);
snprintf(path, 512, “/tmp/audit-%d”, pid);

fdout = open(path, O_RDWR | O_CREAT);
if(fdout == -1)
err(-1, “OPEN”);

quit = 0;
while (!waitpid(pid, &quit, WNOHANG)) {

/* Audit pipe input. */
read_auditpipe(fd, fdout);

/*
* See whether is there anything on the shared-memory,
* if so build a structure
*/
sem_getvalue(mutex, &count);
if(count) {
shared_string = read_string();
elem = malloc(sizeof(struct sysctl_record));
if(elem == NULL)
err(-1, “MALLOC”);

while((count = parse_string(shared_string)) != -1) {
descr = get_descr(shared_string);
if(count == INT_TYPE) {
if(!strncmp(descr, “arg1″, 4))
elem->mib[1] = get_int(shared_string);
else if (!strncmp(descr, “arg”, 3))
elem->mib[0] = get_int(shared_string);
else
elem->ret = get_int(shared_string);
}
else if ( count == STRING_TYPE) {
snprintf(elem->err_val, sizeof(elem->err_val) >
strlen(shared_string) +1 ?
strlen(shared_string) +1 :
sizeof(elem->err_val), “%s”,
shared_string);
}
shared_string = read_string();
}
head = add_field(head, elem);
if(head == NULL)
err(-1, “List error”);
}
}
}
close_auditpipe(fd, fdout);
end_channel();
f = init_log(pid);
validate(f, head, path, pid);
fclose(f);
return (0);
}

As you might see, the validate() function will compare records against collected data, whereas the test_sysctl() will send  to the parent, using shared memory, information regarding the events. You might see a sort of  ”API” here, those functions were provided in order to ease tests writing. They are somehow commented, so you might find useful to take a look at the code. I wrote tests for 20 events, but theoretically they are 500+, so we need your help. As usual if you have any kind of suggestions or comments feel free to contact me. Here: http://www1.autistici.org/snagg/final.zip you can find the whole code.  Cheers,Snagg