| /* |
| decode_pptp.c |
| |
| Microsoft PPTP MS-CHAP. Derived from Aleph One's anger.c. |
| |
| Copyright (c) 2000 Dug Song <dugsong@monkey.org> |
| Copyright (c) 2000 Aleph One <aleph1@securityfocus.com> |
| |
| $Id: decode_pptp.c,v 1.3 2000/12/15 20:16:58 dugsong Exp $ |
| */ |
| |
| #include "config.h" |
| |
| #include <sys/types.h> |
| #include <openssl/sha.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include "buf.h" |
| #include "decode.h" |
| |
| struct pptp_gre_header { |
| u_char flags; /* bitfield */ |
| u_char ver; /* should be PPTP_GRE_VER (enhanced GRE) */ |
| u_short protocol; /* should be PPTP_GRE_PROTO (ppp-encaps) */ |
| u_short payload_len; /* size of ppp payload, not inc. gre header */ |
| u_short call_id; /* peer's call_id for this session */ |
| u_int32_t seq; /* sequence number. Present if S==1 */ |
| u_int32_t ack; /* seq number of highest packet recieved by */ |
| /* sender in this session */ |
| }; |
| |
| #define PPTP_GRE_PROTO 0x880B |
| #define PPTP_GRE_VER 0x1 |
| |
| #define PPTP_GRE_IS_C(f) ((f) & 0x80) |
| #define PPTP_GRE_IS_R(f) ((f) & 0x40) |
| #define PPTP_GRE_IS_K(f) ((f) & 0x20) |
| #define PPTP_GRE_IS_S(f) ((f) & 0x10) |
| #define PPTP_GRE_IS_A(f) ((f) & 0x80) |
| |
| struct ppp_header { |
| u_char address; |
| u_char control; |
| u_short proto; |
| }; |
| |
| #define PPP_PROTO_CHAP 0xc223 |
| |
| struct ppp_lcp_chap_header { |
| u_char code; |
| u_char ident; |
| u_short length; |
| }; |
| |
| #define PPP_CHAP_CODE_CHALLENGE 1 |
| #define PPP_CHAP_CODE_RESPONSE 2 |
| |
| struct ppp_chap_challenge { |
| u_char size; |
| union { |
| u_char challenge_v1[8]; |
| u_char challenge_v2[16]; |
| struct { |
| u_char lanman[24]; |
| u_char nt[24]; |
| u_char flag; |
| } response_v1; |
| struct { |
| u_char peer_challenge[16]; |
| u_char reserved[8]; |
| u_char nt[24]; |
| u_char flag; |
| } response_v2; |
| } value; |
| /* name */ |
| }; |
| |
| struct challenge { |
| u_char version; |
| u_char challenge[16]; |
| }; |
| |
| int |
| decode_pptp(u_char *buf, int len, u_char *obuf, int olen) |
| { |
| static struct challenge save_challenge; |
| struct buf outbuf; |
| struct pptp_gre_header *pgh; |
| struct ppp_header *ppp; |
| struct ppp_lcp_chap_header *chap; |
| struct ppp_chap_challenge *chapch; |
| u_short proto; |
| u_char *p, name[64], digest[SHA_DIGEST_LENGTH]; |
| SHA_CTX ctx; |
| int i, pghlen; |
| |
| buf_init(&outbuf, obuf, olen); |
| |
| if (len < (pghlen = sizeof(*pgh))) |
| return (0); |
| |
| pgh = (struct pptp_gre_header *)buf; |
| |
| if ((pgh->ver & 0x7f) != PPTP_GRE_VER || |
| ntohs(pgh->protocol) != PPTP_GRE_PROTO || |
| PPTP_GRE_IS_C(pgh->flags) || PPTP_GRE_IS_R(pgh->flags) || |
| PPTP_GRE_IS_K(pgh->flags) == 0 || (pgh->flags & 0xf) != 0) { |
| return (0); |
| } |
| if (PPTP_GRE_IS_S(pgh->flags) == 0) |
| return (0); |
| |
| if (PPTP_GRE_IS_A(pgh->ver) == 0) |
| pghlen -= sizeof(pgh->ack); |
| |
| if (len - pghlen < ntohs(pgh->payload_len)) |
| return (0); |
| |
| ppp = (struct ppp_header *)(pgh + 1); |
| |
| if (ppp->address != 0xff && ppp->control != 0x3) { |
| proto = pntohs(ppp); |
| chap = (struct ppp_lcp_chap_header *) |
| ((u_char *)ppp + sizeof(proto)); |
| } |
| else { |
| proto = ntohs(ppp->proto); |
| chap = (struct ppp_lcp_chap_header *)(ppp + 1); |
| } |
| if (proto != PPP_PROTO_CHAP) |
| return (0); |
| |
| switch (chap->code) { |
| |
| case PPP_CHAP_CODE_CHALLENGE: |
| chapch = (struct ppp_chap_challenge *)(chap + 1); |
| |
| if (chapch->size == 8) { |
| save_challenge.version = 1; |
| memcpy(save_challenge.challenge, |
| chapch->value.challenge_v1, 8); |
| } |
| else if (chapch->size == 16) { |
| save_challenge.version = 2; |
| memcpy(save_challenge.challenge, |
| chapch->value.challenge_v2, 16); |
| } |
| else save_challenge.version = 0; |
| break; |
| |
| case PPP_CHAP_CODE_RESPONSE: |
| if (save_challenge.version == 0) |
| break; |
| |
| chapch = (struct ppp_chap_challenge *)(chap + 1); |
| i = ntohs(chap->length) - 54; |
| if (i > 63) i = 63; |
| memcpy(name, (u_char *)chap + 54, i); |
| name[i] = '\0'; |
| |
| buf_putf(&outbuf, "%s:0:", name); |
| |
| if (save_challenge.version == 1) { |
| for (i = 0; i < 8; i++) { |
| buf_putf(&outbuf, "%02X", |
| save_challenge.challenge[i]); |
| } |
| buf_put(&outbuf, ":", 1); |
| |
| for (i = 0; i < 24; i++) { |
| buf_putf(&outbuf, "%02X", |
| chapch->value.response_v1.lanman[i]); |
| } |
| buf_put(&outbuf, ":", 1); |
| |
| for (i = 0; i < 24; i++) { |
| buf_putf(&outbuf, "%02X", |
| chapch->value.response_v1.nt[i]); |
| } |
| buf_put(&outbuf, "\n", 1); |
| } |
| else if (save_challenge.version == 2) { |
| chapch = (struct ppp_chap_challenge *)(chap + 1); |
| if ((p = strchr(name, '\\')) == NULL) |
| p = name; |
| |
| SHA1_Init(&ctx); |
| SHA1_Update(&ctx, chapch->value.response_v2.peer_challenge, 16); |
| SHA1_Update(&ctx, save_challenge.challenge, 16); |
| SHA1_Update(&ctx, p, strlen(p)); |
| SHA1_Final(digest, &ctx); |
| |
| for (i = 0; i < 8; i++) { |
| buf_putf(&outbuf, "%02X", digest[i]); |
| } |
| buf_putf(&outbuf, ":000000000000000000000000000000000000000000000000:"); |
| for (i = 0; i < 24; i++) { |
| buf_putf(&outbuf, "%02X", |
| chapch->value.response_v2.nt[i]); |
| } |
| buf_put(&outbuf, "\n", 1); |
| |
| save_challenge.version = 0; |
| } |
| break; |
| } |
| buf_end(&outbuf); |
| |
| return (buf_len(&outbuf)); |
| } |
| |