1/* $Id: upnppermissions.c,v 1.18 2014/03/07 10:43:29 nanard Exp $ */ 2/* MiniUPnP project 3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 * (c) 2006-2014 Thomas Bernard 5 * This software is subject to the conditions detailed 6 * in the LICENCE file provided within the distribution */ 7 8#include <ctype.h> 9#include <string.h> 10#include <stdlib.h> 11#include <stdio.h> 12#include <syslog.h> 13#include <netinet/in.h> 14#include <arpa/inet.h> 15#include <unistd.h> 16#include "config.h" 17#include "upnppermissions.h" 18 19/* read_permission_line() 20 * parse the a permission line which format is : 21 * (deny|allow) [0-9]+(-[0-9]+) ip/mask [0-9]+(-[0-9]+) 22 * ip/mask is either 192.168.1.1/24 or 192.168.1.1/255.255.255.0 23 */ 24int 25read_permission_line(struct upnpperm * perm, 26 char * p) 27{ 28 char * q; 29 int n_bits; 30 int i; 31 32 /* first token: (allow|deny) */ 33 while(isspace(*p)) 34 p++; 35 if(0 == memcmp(p, "allow", 5)) 36 { 37 perm->type = UPNPPERM_ALLOW; 38 p += 5; 39 } 40 else if(0 == memcmp(p, "deny", 4)) 41 { 42 perm->type = UPNPPERM_DENY; 43 p += 4; 44 } 45 else 46 { 47 return -1; 48 } 49 while(isspace(*p)) 50 p++; 51 52 /* second token: eport or eport_min-eport_max */ 53 if(!isdigit(*p)) 54 return -1; 55 for(q = p; isdigit(*q); q++); 56 if(*q=='-') 57 { 58 *q = '\0'; 59 i = atoi(p); 60 if(i > 65535) 61 return -1; 62 perm->eport_min = (u_short)i; 63 q++; 64 p = q; 65 while(isdigit(*q)) 66 q++; 67 *q = '\0'; 68 i = atoi(p); 69 if(i > 65535) 70 return -1; 71 perm->eport_max = (u_short)i; 72 if(perm->eport_min > perm->eport_max) 73 return -1; 74 } 75 else if(isspace(*q)) 76 { 77 *q = '\0'; 78 i = atoi(p); 79 if(i > 65535) 80 return -1; 81 perm->eport_min = perm->eport_max = (u_short)i; 82 } 83 else 84 { 85 return -1; 86 } 87 p = q + 1; 88 while(isspace(*p)) 89 p++; 90 91 /* third token: ip/mask */ 92 if(!isdigit(*p)) 93 return -1; 94 for(q = p; isdigit(*q) || (*q == '.'); q++); 95 if(*q=='/') 96 { 97 *q = '\0'; 98 if(!inet_aton(p, &perm->address)) 99 return -1; 100 q++; 101 p = q; 102 while(isdigit(*q)) 103 q++; 104 if(*q == '.') 105 { 106 while(*q == '.' || isdigit(*q)) 107 q++; 108 if(!isspace(*q)) 109 return -1; 110 *q = '\0'; 111 if(!inet_aton(p, &perm->mask)) 112 return -1; 113 } 114 else if(!isspace(*q)) 115 return -1; 116 else 117 { 118 *q = '\0'; 119 n_bits = atoi(p); 120 if(n_bits > 32) 121 return -1; 122 perm->mask.s_addr = htonl(n_bits ? (0xffffffffu << (32 - n_bits)) : 0); 123 } 124 } 125 else if(isspace(*q)) 126 { 127 *q = '\0'; 128 if(!inet_aton(p, &perm->address)) 129 return -1; 130 perm->mask.s_addr = 0xffffffffu; 131 } 132 else 133 { 134 return -1; 135 } 136 p = q + 1; 137 138 /* fourth token: iport or iport_min-iport_max */ 139 while(isspace(*p)) 140 p++; 141 if(!isdigit(*p)) 142 return -1; 143 for(q = p; isdigit(*q); q++); 144 if(*q=='-') 145 { 146 *q = '\0'; 147 i = atoi(p); 148 if(i > 65535) 149 return -1; 150 perm->iport_min = (u_short)i; 151 q++; 152 p = q; 153 while(isdigit(*q)) 154 q++; 155 *q = '\0'; 156 i = atoi(p); 157 if(i > 65535) 158 return -1; 159 perm->iport_max = (u_short)i; 160 if(perm->iport_min > perm->iport_max) 161 return -1; 162 } 163 else if(isspace(*q) || *q == '\0') 164 { 165 *q = '\0'; 166 i = atoi(p); 167 if(i > 65535) 168 return -1; 169 perm->iport_min = perm->iport_max = (u_short)i; 170 } 171 else 172 { 173 return -1; 174 } 175#ifdef DEBUG 176 printf("perm rule added : %s %hu-%hu %08x/%08x %hu-%hu\n", 177 (perm->type==UPNPPERM_ALLOW)?"allow":"deny", 178 perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr), 179 ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max); 180#endif 181 return 0; 182} 183 184#ifdef USE_MINIUPNPDCTL 185void 186write_permlist(int fd, const struct upnpperm * permary, 187 int nperms) 188{ 189 int l; 190 const struct upnpperm * perm; 191 int i; 192 char buf[128]; 193 write(fd, "Permissions :\n", 14); 194 for(i = 0; i<nperms; i++) 195 { 196 perm = permary + i; 197 l = snprintf(buf, sizeof(buf), "%02d %s %hu-%hu %08x/%08x %hu-%hu\n", 198 i, 199 (perm->type==UPNPPERM_ALLOW)?"allow":"deny", 200 perm->eport_min, perm->eport_max, ntohl(perm->address.s_addr), 201 ntohl(perm->mask.s_addr), perm->iport_min, perm->iport_max); 202 if(l<0) 203 return; 204 write(fd, buf, l); 205 } 206} 207#endif 208 209/* match_permission() 210 * returns: 1 if eport, address, iport matches the permission rule 211 * 0 if no match */ 212static int 213match_permission(const struct upnpperm * perm, 214 u_short eport, struct in_addr address, u_short iport) 215{ 216 if( (eport < perm->eport_min) || (perm->eport_max < eport)) 217 return 0; 218 if( (iport < perm->iport_min) || (perm->iport_max < iport)) 219 return 0; 220 if( (address.s_addr & perm->mask.s_addr) 221 != (perm->address.s_addr & perm->mask.s_addr) ) 222 return 0; 223 return 1; 224} 225 226#if 0 227/* match_permission_internal() 228 * returns: 1 if address, iport matches the permission rule 229 * 0 if no match */ 230static int 231match_permission_internal(const struct upnpperm * perm, 232 struct in_addr address, u_short iport) 233{ 234 if( (iport < perm->iport_min) || (perm->iport_max < iport)) 235 return 0; 236 if( (address.s_addr & perm->mask.s_addr) 237 != (perm->address.s_addr & perm->mask.s_addr) ) 238 return 0; 239 return 1; 240} 241#endif 242 243int 244check_upnp_rule_against_permissions(const struct upnpperm * permary, 245 int n_perms, 246 u_short eport, struct in_addr address, 247 u_short iport) 248{ 249 int i; 250 for(i=0; i<n_perms; i++) 251 { 252 if(match_permission(permary + i, eport, address, iport)) 253 { 254 syslog(LOG_DEBUG, 255 "UPnP permission rule %d matched : port mapping %s", 256 i, (permary[i].type == UPNPPERM_ALLOW)?"accepted":"rejected" 257 ); 258 return (permary[i].type == UPNPPERM_ALLOW); 259 } 260 } 261 syslog(LOG_DEBUG, "no permission rule matched : accept by default (n_perms=%d)", n_perms); 262 return 1; /* Default : accept */ 263} 264 265