/* * Layer Two Tunnelling Protocol Daemon * Copyright (C) 1998 Adtran, Inc. * Copyright (C) 2002 Jeff McAdams * * Mark Spencer * * This software is distributed under the terms * of the GPL, which you should have received * along with this source. * * Authorization, Accounting, and Access control * */ #include #include #include #include #include #include #include "l2tp.h" extern void bufferDump (char *, int); /* FIXME: Accounting? */ struct addr_ent *uaddr[ADDR_HASH_SIZE]; void init_addr () { int x; for (x = 0; x < ADDR_HASH_SIZE; x++) uaddr[x] = NULL; } static int ip_used (unsigned int addr) { struct addr_ent *tmp; tmp = uaddr[addr % ADDR_HASH_SIZE]; while (tmp) { if (tmp->addr == addr) return -1; tmp = tmp->next; } return 0; } void mk_challenge (char *c, int length) { get_entropy(c, length); /* int x; int *s = (int *) c; for (x = 0; x < length / sizeof (int); x++) s[x] = rand (); */ } void reserve_addr (unsigned int addr) { /* Mark this address as in use */ struct addr_ent *tmp, *tmp2; addr = ntohl (addr); if (ip_used (addr)) return; tmp = uaddr[addr % ADDR_HASH_SIZE]; tmp2 = (struct addr_ent *) malloc (sizeof (struct addr_ent)); uaddr[addr % ADDR_HASH_SIZE] = tmp2; tmp2->next = tmp; tmp2->addr = addr; } void unreserve_addr (unsigned int addr) { struct addr_ent *tmp, *last = NULL, *z; addr = ntohl (addr); tmp = uaddr[addr % ADDR_HASH_SIZE]; while (tmp) { if (tmp->addr == addr) { if (last) { last->next = tmp->next; } else { uaddr[addr % ADDR_HASH_SIZE] = tmp->next; } z = tmp; tmp = tmp->next; free (z); } else { last = tmp; tmp = tmp->next; } } } unsigned int get_addr (struct iprange *ipr) { unsigned int x, y; int status; struct iprange *ipr2; while (ipr) { if (ipr->sense == SENSE_ALLOW) for (x = ntohl (ipr->start); x <= ntohl (ipr->end); x++) { /* Found an IP in an ALLOW range, check to be sure it is consistant through the remaining regions */ if (!ip_used (x)) { status = SENSE_ALLOW; ipr2 = ipr->next; while (ipr2) { if ((x >= ntohl (ipr2->start)) && (x <= ntohl (ipr2->end))) status = ipr2->sense; ipr2 = ipr2->next; } y = htonl (x); if (status == SENSE_ALLOW) return y; } }; ipr = ipr->next; } return 0; } int get_secret (char *us, char *them, char *secret, int size) { FILE *f; char buf[STRLEN]; char *u, *t, *s; int num = 0; f = fopen (gconfig.authfile, "r"); if (!f) { log (LOG_WARN, "%s : Unable to open '%s' for authentication\n", __FUNCTION__, gconfig.authfile); return 0; } while (!feof (f)) { num++; fgets (buf, sizeof (buf), f); if (feof (f)) break; /* Strip comments */ for (t = buf; *t; t++) *t = ((*t == '#') || (*t == ';')) ? 0 : *t; /* Strip trailing whitespace */ for (t = buf + strlen (buf) - 1; (t >= buf) && (*t < 33); t--) *t = 0; if (!strlen (buf)) continue; /* Empty line */ u = buf; while (*u && (*u < 33)) u++; /* us */ if (!*u) { log (LOG_WARN, "%s: Invalid authentication info (no us), line %d\n", __FUNCTION__, num); continue; } t = u; while (*t > 32) t++; *(t++) = 0; while (*t && (*t < 33)) t++; /* them */ if (!*t) { log (LOG_WARN, "%s: Invalid authentication info (nothem), line %d\n", __FUNCTION__, num); continue; } s = t; while (*s > 33) s++; *(s++) = 0; while (*s && (*s < 33)) s++; if (!*s) { log (LOG_WARN, "%s: Invalid authentication info (no secret), line %d\n", __FUNCTION__, num); continue; } if ((!strcasecmp (u, us) || !strcasecmp (u, "*")) && (!strcasecmp (t, them) || !strcasecmp (t, "*"))) { #ifdef DEBUG_AUTH log (LOG_DEBUG, "%s: we are '%s', they are '%s', secret is '%s'\n", __FUNCTION__, u, t, s); #endif strncpy (secret, s, size); return -1; } } return 0; } int handle_challenge (struct tunnel *t, struct challenge *chal) { char *us; char *them; if (!t->lns && !t->lac) { log (LOG_DEBUG, "%s: No LNS or LAC to handle challenge!\n", __FUNCTION__); return -1; } #ifdef DEBUG_AUTH log (LOG_DEBUG, "%s: making response for tunnel: %d\n", __FUNCTION__, t->ourtid); #endif if (t->lns) { if (t->lns->hostname[0]) us = t->lns->hostname; else us = hostname; if (t->lns->peername[0]) them = t->lns->peername; else them = t->hostname; } else { if (t->lac->hostname[0]) us = t->lac->hostname; else us = hostname; if (t->lac->peername[0]) them = t->lac->peername; else them = t->hostname; } if (!get_secret (us, them, chal->secret, sizeof (chal->secret))) { log (LOG_DEBUG, "%s: no secret found for us='%s' and them='%s'\n", __FUNCTION__, us, them); return -1; } #if DEBUG_AUTH log (LOG_DEBUG, "*%s: Here comes the chal->ss:\n", __FUNCTION__); bufferDump (&chal->ss, 1); log (LOG_DEBUG, "%s: Here comes the secret\n", __FUNCTION__); bufferDump (chal->secret, strlen (chal->secret)); log (LOG_DEBUG, "%s: Here comes the challenge\n", __FUNCTION__); bufferDump (chal->challenge, strlen (chal->challenge)); #endif memset (chal->response, 0, MD_SIG_SIZE); MD5Init (&chal->md5); MD5Update (&chal->md5, &chal->ss, 1); MD5Update (&chal->md5, chal->secret, strlen (chal->secret)); MD5Update (&chal->md5, chal->challenge, strlen(chal->challenge)); MD5Final (chal->response, &chal->md5); #ifdef DEBUG_AUTH log (LOG_DEBUG, "response is %X%X%X%X to '%s' and %X%X%X%X, %d\n", *((int *) &chal->response[0]), *((int *) &chal->response[4]), *((int *) &chal->response[8]), *((int *) &chal->response[12]), chal->secret, *((int *) &chal->challenge[0]), *((int *) &chal->challenge[4]), *((int *) &chal->challenge[8]), *((int *) &chal->challenge[12]), chal->ss); #endif chal->state = STATE_CHALLENGED; return 0; } struct lns *get_lns (struct tunnel *t) { /* * Look through our list of LNS's and * find a reasonable LNS for this call * if one is available */ struct lns *lns; struct iprange *ipr; int allow, checkdefault = 0; /* If access control is disabled, we give the default otherwise, we give nothing */ allow = 0; lns = lnslist; if (!lns) { lns = deflns; checkdefault = -1; } while (lns) { ipr = lns->lacs; while (ipr) { if ((ntohl (t->peer.sin_addr.s_addr) >= ntohl (ipr->start)) && (ntohl (t->peer.sin_addr.s_addr) <= ntohl (ipr->end))) { #ifdef DEBUG_AAA log (LOG_DEBUG, "get_lns: Rule %s to %s, sense %s matched %s\n", IPADDY (ipr->start), IPADDY (ipr->end), (ipr->sense ? "allow" : "deny"), IPADDY (t->addr)); #endif allow = ipr->sense; } ipr = ipr->next; } if (allow) return lns; lns = lns->next; if (!lns && !checkdefault) { lns = deflns; checkdefault = -1; } } if (gconfig.accesscontrol) return NULL; else return deflns; } #ifdef DEBUG_HIDDEN void print_md5 (void *md5) { int *i = (int *) md5; log (LOG_DEBUG, "%X%X%X%X\n", i[0], i[1], i[2], i[3], i[4]); } inline void print_challenge (struct challenge *chal) { log (LOG_DEBUG, "vector: "); print_md5 (chal->vector); log (LOG_DEBUG, "secret: %s\n", chal->secret); } #endif void encrypt_avp (struct buffer *buf, _u16 len, struct tunnel *t) { /* Encrypts an AVP of len, at data. We assume there are two "spare bytes" before the data pointer,l but otherwise this is just a normal AVP that is about to be returned from an avpsend routine */ struct avp_hdr *new_hdr = (struct avp_hdr *) (buf->start + buf->len - len); struct avp_hdr *old_hdr = (struct avp_hdr *) (buf->start + buf->len - len + 2); _u16 length, flags, attr; /* New length, old flags */ char *ptr, *end; int cnt; unsigned char digest[MD_SIG_SIZE]; unsigned char *previous_segment; /* FIXME: Should I pad more randomly? Right now I pad to nearest 16 bytes */ length = ((len - sizeof (struct avp_hdr) + 1) / 16 + 1) * 16 + sizeof (struct avp_hdr); flags = htons (old_hdr->length) & 0xF000; new_hdr->length = htons (length | flags | HBIT); new_hdr->vendorid = old_hdr->vendorid; new_hdr->attr = attr = old_hdr->attr; /* This is really the length field of the hidden sub-format */ old_hdr->attr = htons (len - sizeof (struct avp_hdr)); /* Okay, now we've rewritten the header, as it should be. Let's start encrypting the actual data now */ buf->len -= len; buf->len += length; /* Back to the beginning of real data, including the original length AVP */ MD5Init (&t->chal_them.md5); MD5Update (&t->chal_them.md5, (void *) &attr, 2); MD5Update (&t->chal_them.md5, t->chal_them.secret, strlen (t->chal_them.secret)); MD5Update (&t->chal_them.md5, t->chal_them.vector, VECTOR_SIZE); MD5Final (digest, &t->chal_them.md5); /* Though not a "MUST" in the spec, our subformat length is always a multiple of 16 */ ptr = ((char *) new_hdr) + sizeof (struct avp_hdr); end = ((char *) new_hdr) + length; previous_segment = ptr; while (ptr < end) { #if DEBUG_HIDDEN log (LOG_DEBUG, "%s: The digest to be XOR'ed\n", __FUNCTION__); bufferDump (digest, MD_SIG_SIZE); log (LOG_DEBUG, "%s: The plaintext to be XOR'ed\n", __FUNCTION__); bufferDump (ptr, MD_SIG_SIZE); #endif for (cnt = 0; cnt < MD_SIG_SIZE; cnt++, ptr++) { *ptr = *ptr ^ digest[cnt]; } #if DEBUG_HIDDEN log (LOG_DEBUG, "%s: The result of XOR\n", __FUNCTION__); bufferDump (previous_segment, MD_SIG_SIZE); #endif if (ptr < end) { MD5Init (&t->chal_them.md5); MD5Update (&t->chal_them.md5, t->chal_them.secret, strlen (t->chal_them.secret)); MD5Update (&t->chal_them.md5, previous_segment, MD_SIG_SIZE); MD5Final (digest, &t->chal_them.md5); } previous_segment = ptr; } } int decrypt_avp (char *buf, struct tunnel *t) { /* Decrypts a hidden AVP pointed to by buf. The new header will be exptected to be two characters offset from the old */ int cnt = 0; int len, olen, flags; char digest[MD_SIG_SIZE]; char *ptr, *end; _u16 attr; struct avp_hdr *old_hdr = (struct avp_hdr *) buf; struct avp_hdr *new_hdr = (struct avp_hdr *) (buf + 2); int saved_segment_len; /* maybe less 16; may be used if the cipher is longer than 16 octets */ char saved_segment[MD_SIG_SIZE]; ptr = ((char *) old_hdr) + sizeof (struct avp_hdr); olen = old_hdr->length & 0x0FFF; end = buf + olen; if (!t->chal_us.vector) { log (LOG_DEBUG, "decrypt_avp: Hidden bit set, but no random vector specified!\n"); return -EINVAL; } /* First, let's decrypt all the data. We're not guaranteed that it will be padded to a 16 byte boundary, so we have to be more careful than when encrypting */ attr = ntohs (old_hdr->attr); MD5Init (&t->chal_us.md5); MD5Update (&t->chal_us.md5, (void *) &attr, 2); MD5Update (&t->chal_us.md5, t->chal_us.secret, strlen (t->chal_us.secret)); MD5Update (&t->chal_us.md5, t->chal_us.vector, t->chal_us.vector_len); MD5Final (digest, &t->chal_us.md5); #ifdef DEBUG_HIDDEN log (LOG_DEBUG, "attribute is %d and challenge is: ", attr); print_challenge (&t->chal_us); log (LOG_DEBUG, "md5 is: "); print_md5 (digest); #endif while (ptr < end) { if (cnt >= MD_SIG_SIZE) { MD5Init (&t->chal_us.md5); MD5Update (&t->chal_us.md5, t->chal_us.secret, strlen (t->chal_us.secret)); MD5Update (&t->chal_us.md5, saved_segment, MD_SIG_SIZE); MD5Final (digest, &t->chal_us.md5); cnt = 0; } /* at the beginning of each segment, we save the current segment (16 octets or less) of cipher * so that the next round of MD5 (if there is a next round) hash could use it */ if (cnt == 0) { saved_segment_len = (end - ptr < MD_SIG_SIZE) ? (end - ptr) : MD_SIG_SIZE; memcpy (saved_segment, ptr, saved_segment_len); } *ptr = *ptr ^ digest[cnt++]; ptr++; } /* Hopefully we're all nice and decrypted now. Let's rewrite the header. First save the old flags, and get the new stuff */ flags = old_hdr->length & 0xF000 & ~HBIT; len = ntohs (new_hdr->attr) + sizeof (struct avp_hdr); if (len > olen - 2) { log (LOG_DEBUG, "decrypt_avp: Decrypted length is too long (%d > %d)\n", len, olen - 2); return -EINVAL; } new_hdr->attr = old_hdr->attr; new_hdr->vendorid = old_hdr->vendorid; new_hdr->length = len | flags; return 0; }