blob: 7fb058665661e513292ab8917480f7b89a138703 [file] [log] [blame]
/* RPC extension for IP connection matching, Version 2.2
* (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
* - original rpc tracking module
* - "recent" connection handling for kernel 2.3+ netfilter
*
* (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
* - upgraded conntrack modules to oldnat api - kernel 2.4.0+
*
* (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
* - upgraded conntrack modules to newnat api - kernel 2.4.20+
* - extended matching to support filtering on procedures
*
* libipt_rpc.c,v 2.2 2003/01/12 18:30:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
**
* Userspace library syntax:
* --rpc [--rpcs procedure1,procedure2,...procedure128] [--static]
*
* Procedures can be supplied in either numeric or named formats.
* Without --rpcs, this module will behave as the old record-rpc.
**
* Note to all:
*
* RPCs should not be exposed to the internet - ask the Pentagon;
*
* "The unidentified crackers pleaded guilty in July to charges
* of juvenile delinquency stemming from a string of Pentagon
* network intrusions in February.
*
* The youths, going by the names TooShort and Makaveli, used
* a common server security hole to break in, according to
* Dane Jasper, owner of the California Internet service
* provider, Sonic. They used the hole, known as the 'statd'
* exploit, to attempt more than 800 break-ins, Jasper said."
*
* From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998
* URL: http://www.wired.com/news/politics/0,1283,16098,00.html
**
*/
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <rpc/rpc.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ipt_rpc.h>
#include <time.h>
const int IPT_RPC_RPCS = 1;
const int IPT_RPC_STRC = 2;
const int IPT_RPC_INT_LBL = 1;
const int IPT_RPC_INT_NUM = 2;
const int IPT_RPC_INT_BTH = 3;
const int IPT_RPC_CHAR_LEN = 11;
const int IPT_RPC_MAX_ENTS = 128;
const char preerr[11] = "RPC match:";
static int k_itoa(char *string, int number)
{
int maxoctet = IPT_RPC_CHAR_LEN - 1;
int store[IPT_RPC_CHAR_LEN];
int counter;
for (counter=0 ; maxoctet != 0 && number != 0; counter++, maxoctet--) {
store[counter] = number / 10;
store[counter] = number - ( store[counter] * 10 );
number = number / 10;
}
for ( ; counter != 0; counter--, string++)
*string = store[counter - 1] + 48;
*string = 0;
return(0);
}
static int k_atoi(signed char *string)
{
unsigned int result = 0;
int maxoctet = IPT_RPC_CHAR_LEN;
for ( ; *string != 0 && maxoctet != 0; maxoctet--, string++) {
if (*string < 0)
return(0);
if (*string == 0)
break;
if (*string < 48 || *string > 57) {
return(0);
}
result = result * 10 + ( *string - 48 );
}
return(result);
}
static void print_rpcs(char *c_procs, int i_procs, int labels)
{
int proc_ctr;
char *proc_ptr;
unsigned int proc_num;
struct rpcent *rpcent;
for (proc_ctr=0; proc_ctr <= i_procs; proc_ctr++) {
if ( proc_ctr != 0 )
printf(",");
proc_ptr = c_procs;
proc_ptr += proc_ctr * IPT_RPC_CHAR_LEN;
proc_num = k_atoi(proc_ptr);
/* labels(1) == no labels, only numbers
* labels(2) == no numbers, only labels
* labels(3) == both labels and numbers
*/
if (labels == IPT_RPC_INT_LBL || labels == IPT_RPC_INT_BTH ) {
if ( (rpcent = getrpcbynumber(proc_num)) == NULL )
printf("unknown");
else
printf("%s", rpcent->r_name);
}
if (labels == IPT_RPC_INT_BTH )
printf("(");
if (labels == IPT_RPC_INT_NUM || labels == IPT_RPC_INT_BTH )
printf("%i", proc_num);
if (labels == IPT_RPC_INT_BTH )
printf(")");
}
}
static void help(void)
{
printf(
"RPC v%s options:\n"
" --rpcs list,of,procedures"
"\ta list of rpc program numbers to apply\n"
"\t\t\t\tie. 100003,mountd,rquotad (numeric or\n"
"\t\t\t\tname form; see /etc/rpc).\n"
" --strict"
"\t\t\ta flag to force the drop of packets\n"
"\t\t\t\tnot containing \"get\" portmapper requests.\n",
IPTABLES_VERSION);
}
static struct option opts[] = {
{ "rpcs", 1, 0, '1'},
{ "strict", 0, 0, '2'},
{0}
};
static void init(struct ipt_entry_match *match, unsigned int *nfcache)
{
struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
/* caching not yet implemented */
*nfcache |= NFC_UNKNOWN;
/* initialise those funky user vars */
rpcinfo->i_procs = -1;
rpcinfo->strict = 0;
memset((char *)rpcinfo->c_procs, 0, sizeof(rpcinfo->c_procs));
}
static void parse_rpcs_string(char *string, struct ipt_entry_match **match)
{
char err1[64] = "%s invalid --rpcs option-set: `%s' (at character %i)";
char err2[64] = "%s unable to resolve rpc name entry: `%s'";
char err3[64] = "%s maximum number of --rpc options (%i) exceeded";
char buf[256];
char *dup = buf;
int idup = 0;
int term = 0;
char *src, *dst;
char *c_procs;
struct rpcent *rpcent_ptr;
struct ipt_rpc_info *rpcinfo = (struct ipt_rpc_info *)(*match)->data;
memset(buf, 0, sizeof(buf));
for (src=string, dst=buf; term != 1 ; src++, dst++) {
if ( *src != ',' && *src != '\0' ) {
if ( ( *src >= 65 && *src <= 90 ) || ( *src >= 97 && *src <= 122) ) {
*dst = *src;
idup = 1;
} else if ( *src >= 48 && *src <= 57 ) {
*dst = *src;
} else {
exit_error(PARAMETER_PROBLEM, err1, preerr,
string, src - string + 1);
}
} else {
*dst = '\0';
if ( idup == 1 ) {
if ( (rpcent_ptr = getrpcbyname(dup)) == NULL )
exit_error(PARAMETER_PROBLEM, err2,
preerr, dup);
idup = rpcent_ptr->r_number;
} else {
idup = k_atoi(dup);
}
rpcinfo->i_procs++;
if ( rpcinfo->i_procs > IPT_RPC_MAX_ENTS )
exit_error(PARAMETER_PROBLEM, err3, preerr,
IPT_RPC_MAX_ENTS);
c_procs = (char *)rpcinfo->c_procs;
c_procs += rpcinfo->i_procs * IPT_RPC_CHAR_LEN;
memset(buf, 0, sizeof(buf));
k_itoa((char *)dup, idup);
strcpy(c_procs, dup);
if ( *src == '\0')
term = 1;
idup = 0;
memset(buf, 0, sizeof(buf));
dst = (char *)buf - 1;
}
}
return;
}
static int parse(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match)
{
struct ipt_rpc_info *rpcinfo = (struct ipt_rpc_info *)(*match)->data;
switch (c)
{
case '1':
if (invert)
exit_error(PARAMETER_PROBLEM,
"%s unexpected '!' with --rpcs\n", preerr);
if (*flags & IPT_RPC_RPCS)
exit_error(PARAMETER_PROBLEM,
"%s repeated use of --rpcs\n", preerr);
parse_rpcs_string(optarg, match);
*flags |= IPT_RPC_RPCS;
break;
case '2':
if (invert)
exit_error(PARAMETER_PROBLEM,
"%s unexpected '!' with --strict\n", preerr);
if (*flags & IPT_RPC_STRC)
exit_error(PARAMETER_PROBLEM,
"%s repeated use of --strict\n", preerr);
rpcinfo->strict = 1;
*flags |= IPT_RPC_STRC;
break;
default:
return 0;
}
return 1;
}
static void final_check(unsigned int flags)
{
if (flags != (flags | IPT_RPC_RPCS)) {
printf("%s option \"--rpcs\" was not used ... reverting ", preerr);
printf("to old \"record-rpc\" functionality ..\n");
}
}
static void print(const struct ipt_ip *ip,
const struct ipt_entry_match *match,
int numeric)
{
struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
printf("RPCs");
if(rpcinfo->strict == 1)
printf("[strict]");
printf(": ");
if(rpcinfo->i_procs == -1) {
printf("any(*)");
} else {
print_rpcs((char *)&rpcinfo->c_procs, rpcinfo->i_procs, IPT_RPC_INT_BTH);
}
printf(" ");
}
static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
{
struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
if(rpcinfo->i_procs > -1) {
printf("--rpcs ");
print_rpcs((char *)&rpcinfo->c_procs, rpcinfo->i_procs, IPT_RPC_INT_NUM);
printf(" ");
}
if(rpcinfo->strict == 1)
printf("--strict ");
}
static struct iptables_match rpcstruct = { NULL,
"rpc",
IPTABLES_VERSION,
IPT_ALIGN(sizeof(struct ipt_rpc_info)),
IPT_ALIGN(sizeof(struct ipt_rpc_info)),
&help,
&init,
&parse,
&final_check,
&print,
&save,
opts
};
void _init(void)
{
register_match(&rpcstruct);
}