2
** Copyright (C) 2009-2011 Softwink, Inc.
3
** Copyright (C) 2009-2011 Champ Clark III <champ@softwink.com>
5
** This program is free software; you can redistribute it and/or modify
6
** it under the terms of the GNU General Public License Version 2 as
7
** published by the Free Software Foundation. You may not use, modify or
8
** distribute this program under any other version of the GNU General
11
** This program is distributed in the hope that it will be useful,
12
** but WITHOUT ANY WARRANTY; without even the implied warranty of
13
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
** GNU General Public License for more details.
16
** You should have received a copy of the GNU General Public License
17
** along with this program; if not, write to the Free Software
18
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
* Various re-usable functions.
28
#include "config.h" /* From autoconf */
31
#ifdef HAVE_LIBMYSQLCLIENT_R
32
#include <mysql/mysql.h>
33
MYSQL *mysql, *mysql_logzilla;
44
#include <netinet/in.h>
45
#include <arpa/inet.h>
47
#include <sys/socket.h>
55
struct _SaganConfig *config;
57
char sagan_path[MAXPATH];
64
/************************************************
65
* Drop priv's so we aren't running as "root". *
66
************************************************/
68
void droppriv(const char *username, const char *fifo)
71
struct passwd *pw = NULL;
74
pw = getpwnam(username);
78
if (pw->pw_dir) snprintf(sagan_path, sizeof(sagan_path), "%s", pw->pw_dir);
82
if (chroot(pw->pw_dir) != 0 || chdir ("/") != 0) {
83
sagan_log(1, "Could not chroot/chdir to '%.64s'.", pw->pw_dir);
88
/* Some syslog daemons re-open the FIFO as 'root'. We reset that here */
90
ret = chown(config->sagan_fifo, (unsigned long)pw->pw_uid,(unsigned long)pw->pw_gid);
91
if ( ret < 0 ) sagan_log(1, "[%s, line %d] Cannot change ownership of %s to username %s", __FILE__, __LINE__, config->sagan_fifo, username);
93
ret = chown(config->sagan_log_filepath, (unsigned long)pw->pw_uid,(unsigned long)pw->pw_gid);
94
if ( ret < 0 ) sagan_log(1, "[%s, line %d] Cannot change ownership of %s to username %s", __FILE__, __LINE__, config->sagan_log_filepath, username);
96
if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
97
setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
98
sagan_log(1, "[%s, line %d] Could not change to '%.32s' uid=%lu gid=%lu.", __FILE__, __LINE__, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid, pw->pw_dir);
102
sagan_log(1, "[%s, line %d] User \"%.32s\" cannot be found.", __FILE__, __LINE__, username);
105
sagan_log(0, "Dropping privileges [UID: %lu GID: %lu]", (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid);
108
/***************************************************************/
109
/* Convert syslog data to hex for input into the payload table */
110
/***************************************************************/
112
char *fasthex(char *xdata, int length)
114
char conv[] = "0123456789ABCDEF";
121
end = xdata + length;
122
retbuf = (char *) calloc((length*2)+1, sizeof(char));
127
*ridx++ = conv[((*index & 0xFF)>>4)];
128
*ridx++ = conv[((*index & 0xFF)&0x0F)];
135
/* Removes quotes from msg, pcre, etc */
137
char *remquotes(char *s) {
139
for(s1 = s2 = s;*s1;*s1++ = *s2++ )
140
while( *s2 == '"' )s2++;
144
char *remrt(char *s) {
146
for(s1 = s2 = s;*s1;*s1++ = *s2++ )
147
while( *s2 == '\n' )s2++;
153
/* Removes spaces from certain rule fields, etc */
155
char *remspaces(char *s) {
157
for(s1 = s2 = s;*s1;*s1++ = *s2++ )
158
while( *s2 == ' ')s2++;
162
char *toupperc(char* const s) {
165
*cur = toupper(*cur);
172
void sagan_log (int type, const char *format,... ) {
176
va_start(ap, format);
184
strftime(curtime, sizeof(curtime), "%m/%d/%Y %H:%M:%S", now);
186
if ( type == 1 ) chr="E";
188
vsnprintf(buf, sizeof(buf), format, ap);
189
fprintf(config->sagan_log_stream, "[%s] [%s] - %s\n", chr, curtime, buf);
190
fflush(config->sagan_log_stream);
192
if ( programmode == 0 && daemonize == 0) printf("[%s] %s\n", chr, buf);
193
if ( type == 1 ) exit(1);
198
char *p = (char *) &i;
199
if (p[0] == 1) // Lowest address contains the least significant byte
200
return 0; // Little endian
202
return 1; // Big endian
206
/* Converts IP address. For IPv4, we convert the quad IP string to a 32 bit
207
* value. We return the unsigned long value as a pointer to a string because
208
* that's the way IPv6 is done. Basically, we'll probably want IPv6 when
209
* snort supports DB IPv6.
212
int ip2bit (char *ipaddr, int endian) {
214
struct sockaddr_in ipv4;
217
/* Change to AF_UNSPEC for future ipv6 */
218
/* Champ Clark III - 01/18/2011 */
220
if (!inet_pton(AF_INET, ipaddr, &ipv4.sin_addr)) {
221
sagan_log(0, "Warning: inet_pton() error, but continuing...");
225
ip = htonl(ipv4.sin_addr.s_addr);
227
ip = ipv4.sin_addr.s_addr;
233
int isnumeric (char *str) {
235
if(strlen(str) == strspn(str, "0123456789")) {
242
/* Escape SQL. This was taken from Prelude. */
244
#if defined(HAVE_LIBMYSQLCLIENT_R) || defined(HAVE_LIBPQ)
245
char *sql_escape(const char *string, int from )
249
char *escapedre=NULL;
250
char tmpescaped[MAX_SYSLOGMSG];
254
return strdup("NULL");
256
* MySQL documentation say :
257
* The string pointed to by from must be length bytes long. You must
258
* allocate the to buffer to be at least length*2+1 bytes long. (In the
259
* worse case, each character may need to be encoded as using two bytes,
260
* and you need room for the terminating null byte.)
262
len = strlen(string);
264
escaped = malloc(len * 2 + 3);
267
sagan_log(1, "[%s, line %d] Memory exhausted.", __FILE__, __LINE__ );
275
#ifdef HAVE_LIBMYSQLCLIENT_R
276
#if MYSQL_VERSION_ID >= 32200
277
len = mysql_real_escape_string(mysql, escaped + 1, string, len);
279
len = mysql_escape_string(escaped + 1, string, len);
283
escaped[len + 1] = '\'';
284
escaped[len + 2] = '\0';
290
#ifdef HAVE_LIBMYSQLCLIENT_R
291
#if MYSQL_VERSION_ID >= 32200
292
len = mysql_real_escape_string(mysql_logzilla, escaped + 1, string, len);
294
len = mysql_escape_string(escaped + 1, string, len);
297
escaped[len + 1] = '\'';
298
escaped[len + 2] = '\0';
301
/* Temp. copy value, and free(escaped) to prevent mem. leak */
303
snprintf(tmpescaped, sizeof(tmpescaped), "%s", escaped);
304
escapedre=tmpescaped;
311
/* Grab's information between "quotes" and returns it. Use for things like
312
* parsing msg: and pcre */
314
char *betweenquotes(char *instring)
322
for ( i=0; i<strlen(instring); i++) {
324
if ( flag == 1 && instring[i] == '\"' ) flag = 0;
326
snprintf(tmp1, sizeof(tmp1), "%c", instring[i]);
327
strlcat(tmp2, tmp1, sizeof(tmp2));
330
if ( instring[i] == '\"' ) flag++;
338
/* CalcPct (Taken from Snort) */
340
double CalcPct(uint64_t cnt, uint64_t total)
350
pct = (double)cnt / (double)total;
358
/* DNS lookup of hostnames. Wired for IPv4 and IPv6. Code largely
359
* based on Beej's showip.c */
361
char *dns_lookup(char *host)
363
struct addrinfo hints, *res; //,// *p;
365
char ipstr[INET6_ADDRSTRLEN];
369
if ( config->disable_dns_warnings == 0 ) {
370
sagan_log(0, "--------------------------------------------------------------------------");
371
sagan_log(0, "Sagan DNS lookup need for %s.", host);
372
sagan_log(0, "This can affect performance. Please see:" );
373
sagan_log(0, "https://wiki.softwink.com/bin/view/Main/SaganDNS");
374
sagan_log(0, "--------------------------------------------------------------------------");
377
memset(&hints, 0, sizeof hints);
378
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
379
hints.ai_socktype = SOCK_STREAM;
381
if ((status = getaddrinfo(host, NULL, &hints, &res)) != 0) {
382
sagan_log(0, "getaddrinfo: %s", gai_strerror(status));
386
if (res->ai_family == AF_INET) { // IPv4
387
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
388
addr = &(ipv4->sin_addr);
390
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
391
addr = &(ipv6->sin6_addr);
394
inet_ntop(res->ai_family, addr, ipstr, sizeof ipstr);