diff --git a/etc/Makefile.am b/etc/Makefile.am index 245d1a8d..f5864eb3 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -16,7 +16,7 @@ sbindir = @sbindir@ pkgsysconfdir = @pkgsysconfdir@ pkgsysconf_DATA = issue port-id-map radiusclient.conf \ dictionary dictionary.ascend dictionary.compat dictionary.merit \ - dictionary.sip + dictionary.sip dictionary.rfc2868 EXTRA_DIST = issue port-id-map dictionary dictionary.ascend \ dictionary.compat dictionary.merit servers radiusclient.conf.in \ diff --git a/etc/dictionary.rfc2868 b/etc/dictionary.rfc2868 new file mode 100644 index 00000000..0d834cdb --- /dev/null +++ b/etc/dictionary.rfc2868 @@ -0,0 +1,298 @@ +# Contributed by Ghennadi Procopciuc +# + +ATTRIBUTE User-Name 1 string +ATTRIBUTE Password 2 string +ATTRIBUTE CHAP-Password 3 string +ATTRIBUTE NAS-IP-Address 4 ipaddr +ATTRIBUTE NAS-Port-Id 5 integer +ATTRIBUTE Service-Type 6 integer +ATTRIBUTE Framed-Protocol 7 integer +ATTRIBUTE Framed-IP-Address 8 ipaddr +ATTRIBUTE Framed-IP-Netmask 9 ipaddr +ATTRIBUTE Framed-Routing 10 integer +ATTRIBUTE Filter-Id 11 string +ATTRIBUTE Framed-MTU 12 integer +ATTRIBUTE Framed-Compression 13 integer +ATTRIBUTE Login-IP-Host 14 ipaddr +ATTRIBUTE Login-Service 15 integer +ATTRIBUTE Login-TCP-Port 16 integer +ATTRIBUTE Reply-Message 18 string +ATTRIBUTE Callback-Number 19 string +ATTRIBUTE Callback-Id 20 string +ATTRIBUTE Framed-Route 22 string +ATTRIBUTE Framed-IPX-Network 23 ipaddr +ATTRIBUTE State 24 string +ATTRIBUTE Class 25 string +ATTRIBUTE Vendor-Specific 26 string +ATTRIBUTE Session-Timeout 27 integer +ATTRIBUTE Idle-Timeout 28 integer +ATTRIBUTE Termination-Action 29 integer +ATTRIBUTE Called-Station-Id 30 string +ATTRIBUTE Calling-Station-Id 31 string +ATTRIBUTE NAS-Identifier 32 string +ATTRIBUTE Proxy-State 33 string +ATTRIBUTE Login-LAT-Service 34 string +ATTRIBUTE Login-LAT-Node 35 string +ATTRIBUTE Login-LAT-Group 36 string +ATTRIBUTE Framed-AppleTalk-Link 37 integer +ATTRIBUTE Framed-AppleTalk-Network 38 integer +ATTRIBUTE Framed-AppleTalk-Zone 39 string +ATTRIBUTE Acct-Status-Type 40 integer +ATTRIBUTE Acct-Delay-Time 41 integer +ATTRIBUTE Acct-Input-Octets 42 integer +ATTRIBUTE Acct-Output-Octets 43 integer +ATTRIBUTE Acct-Session-Id 44 string +ATTRIBUTE Acct-Authentic 45 integer +ATTRIBUTE Acct-Session-Time 46 integer +ATTRIBUTE Acct-Input-Packets 47 integer +ATTRIBUTE Acct-Output-Packets 48 integer +ATTRIBUTE Acct-Terminate-Cause 49 integer +ATTRIBUTE Acct-Multi-Session-Id 50 string +ATTRIBUTE Acct-Link-Count 51 integer +ATTRIBUTE Acct-Input-Gigawords 52 integer +ATTRIBUTE Acct-Output-Gigawords 53 integer +ATTRIBUTE Event-Timestamp 55 integer +ATTRIBUTE CHAP-Challenge 60 string +ATTRIBUTE NAS-Port-Type 61 integer +ATTRIBUTE Port-Limit 62 integer +ATTRIBUTE Login-LAT-Port 63 integer +ATTRIBUTE Connect-Info 77 string + +# +# RFC3162 IPv6 attributes +# +ATTRIBUTE NAS-IPv6-Address 95 string +ATTRIBUTE Framed-Interface-Id 96 string +ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix +ATTRIBUTE Login-IPv6-Host 98 string +ATTRIBUTE Framed-IPv6-Route 99 string +ATTRIBUTE Framed-IPv6-Pool 100 string + +# +# RFC6911 IPv6 attributes +# +ATTRIBUTE Framed-IPv6-Address 168 ipv6addr +ATTRIBUTE DNS-Server-IPv6-Address 169 ipv6addr +ATTRIBUTE Route-IPv6-Information 170 ipv6prefix + +# +# Experimental Non Protocol Attributes used by Cistron-Radiusd +# +ATTRIBUTE Huntgroup-Name 221 string +ATTRIBUTE User-Category 1029 string +ATTRIBUTE Group-Name 1030 string +ATTRIBUTE Simultaneous-Use 1034 integer +ATTRIBUTE Strip-User-Name 1035 integer +ATTRIBUTE Fall-Through 1036 integer +ATTRIBUTE Add-Port-To-IP-Address 1037 integer +ATTRIBUTE Exec-Program 1038 string +ATTRIBUTE Exec-Program-Wait 1039 string +ATTRIBUTE Hint 1040 string + +# +# Non-Protocol Attributes +# These attributes are used internally by the server +# +ATTRIBUTE Expiration 21 date +ATTRIBUTE Auth-Type 1000 integer +ATTRIBUTE Menu 1001 string +ATTRIBUTE Termination-Menu 1002 string +ATTRIBUTE Prefix 1003 string +ATTRIBUTE Suffix 1004 string +ATTRIBUTE Group 1005 string +ATTRIBUTE Crypt-Password 1006 string +ATTRIBUTE Connect-Rate 1007 integer + +# +# Integer Translations +# + +# User Types + +VALUE Service-Type Login-User 1 +VALUE Service-Type Framed-User 2 +VALUE Service-Type Callback-Login-User 3 +VALUE Service-Type Callback-Framed-User 4 +VALUE Service-Type Outbound-User 5 +VALUE Service-Type Administrative-User 6 +VALUE Service-Type NAS-Prompt-User 7 +VALUE Service-Type Authenticate-Only 8 +VALUE Service-Type Callback-NAS-Prompt 9 +VALUE Service-Type Call-Check 10 +VALUE Service-Type Callback-Administrative 11 + +# Framed Protocols + +VALUE Framed-Protocol PPP 1 +VALUE Framed-Protocol SLIP 2 +VALUE Framed-Protocol ARAP 3 +VALUE Framed-Protocol GANDALF-SLMLP 4 +VALUE Framed-Protocol XYLOGICS-IPX-SLIP 5 +VALUE Framed-Protocol X75 6 + +# Framed Routing Values + +VALUE Framed-Routing None 0 +VALUE Framed-Routing Broadcast 1 +VALUE Framed-Routing Listen 2 +VALUE Framed-Routing Broadcast-Listen 3 + +# Framed Compression Types + +VALUE Framed-Compression None 0 +VALUE Framed-Compression Van-Jacobson-TCP-IP 1 +VALUE Framed-Compression IPX-Header 2 +VALUE Framed-Compression Stac-LZS 3 + +# Login Services + +VALUE Login-Service Telnet 0 +VALUE Login-Service Rlogin 1 +VALUE Login-Service TCP-Clear 2 +VALUE Login-Service PortMaster 3 +VALUE Login-Service LAT 4 +VALUE Login-Service X.25-PAD 5 +VALUE Login-Service X.25-T3POS 6 +VALUE Login-Service TCP-Clear-Quiet 8 + +# Status Types + +VALUE Acct-Status-Type Start 1 +VALUE Acct-Status-Type Stop 2 +VALUE Acct-Status-Type Alive 3 +VALUE Acct-Status-Type Accounting-On 7 +VALUE Acct-Status-Type Accounting-Off 8 + +# Authentication Types + +VALUE Acct-Authentic RADIUS 1 +VALUE Acct-Authentic Local 2 +VALUE Acct-Authentic Remote 3 + +# Termination Options + +VALUE Termination-Action Default 0 +VALUE Termination-Action RADIUS-Request 1 + +# NAS Port Types, available in 3.3.1 and later + +VALUE NAS-Port-Type Async 0 +VALUE NAS-Port-Type Sync 1 +VALUE NAS-Port-Type ISDN 2 +VALUE NAS-Port-Type ISDN-V120 3 +VALUE NAS-Port-Type ISDN-V110 4 +VALUE NAS-Port-Type Virtual 5 +VALUE NAS-Port-Type PIAFS 6 +VALUE NAS-Port-Type HDLC-Clear-Channel 7 +VALUE NAS-Port-Type X.25 8 +VALUE NAS-Port-Type X.75 9 +VALUE NAS-Port-Type G.3-Fax 10 +VALUE NAS-Port-Type SDSL 11 +VALUE NAS-Port-Type ADSL-CAP 12 +VALUE NAS-Port-Type ADSL-DMT 13 +VALUE NAS-Port-Type IDSL 14 +VALUE NAS-Port-Type Ethernet 15 + +# Acct Terminate Causes, available in 3.3.2 and later + +VALUE Acct-Terminate-Cause User-Request 1 +VALUE Acct-Terminate-Cause Lost-Carrier 2 +VALUE Acct-Terminate-Cause Lost-Service 3 +VALUE Acct-Terminate-Cause Idle-Timeout 4 +VALUE Acct-Terminate-Cause Session-Timeout 5 +VALUE Acct-Terminate-Cause Admin-Reset 6 +VALUE Acct-Terminate-Cause Admin-Reboot 7 +VALUE Acct-Terminate-Cause Port-Error 8 +VALUE Acct-Terminate-Cause NAS-Error 9 +VALUE Acct-Terminate-Cause NAS-Request 10 +VALUE Acct-Terminate-Cause NAS-Reboot 11 +VALUE Acct-Terminate-Cause Port-Unneeded 12 +VALUE Acct-Terminate-Cause Port-Preempted 13 +VALUE Acct-Terminate-Cause Port-Suspended 14 +VALUE Acct-Terminate-Cause Service-Unavailable 15 +VALUE Acct-Terminate-Cause Callback 16 +VALUE Acct-Terminate-Cause User-Error 17 +VALUE Acct-Terminate-Cause Host-Request 18 + +# +# Non-Protocol Integer Translations +# + +VALUE Auth-Type Local 0 +VALUE Auth-Type System 1 +VALUE Auth-Type SecurID 2 +VALUE Auth-Type Crypt-Local 3 +VALUE Auth-Type Reject 4 + +# +# Cistron extensions +# +VALUE Auth-Type Pam 253 +VALUE Auth-Type Accept 254 + +# +# Experimental Non-Protocol Integer Translations for Cistron-Radiusd +# +VALUE Fall-Through No 0 +VALUE Fall-Through Yes 1 +VALUE Add-Port-To-IP-Address No 0 +VALUE Add-Port-To-IP-Address Yes 1 + +# +# Configuration Values +# uncomment these two lines to turn account expiration on +# + +#VALUE Server-Config Password-Expiration 30 +#VALUE Server-Config Password-Warning 5 + +# +# RFC2868 attributes and values +# +ATTRIBUTE Tunnel-Type 64 integer has_tag +ATTRIBUTE Tunnel-Medium-Type 65 integer has_tag +ATTRIBUTE Tunnel-Client-Endpoint 66 string has_tag +ATTRIBUTE Tunnel-Server-Endpoint 67 string has_tag + +ATTRIBUTE Tunnel-Password 69 string has_tag,encrypt=2 + +ATTRIBUTE Tunnel-Private-Group-Id 81 string has_tag +ATTRIBUTE Tunnel-Assignment-Id 82 string has_tag +ATTRIBUTE Tunnel-Preference 83 integer has_tag + +ATTRIBUTE Tunnel-Client-Auth-Id 90 string has_tag +ATTRIBUTE Tunnel-Server-Auth-Id 91 string has_tag + +# Tunnel Type + +VALUE Tunnel-Type PPTP 1 +VALUE Tunnel-Type L2F 2 +VALUE Tunnel-Type L2TP 3 +VALUE Tunnel-Type ATMP 4 +VALUE Tunnel-Type VTP 5 +VALUE Tunnel-Type AH 6 +VALUE Tunnel-Type IP 7 +VALUE Tunnel-Type MIN-IP 8 +VALUE Tunnel-Type ESP 9 +VALUE Tunnel-Type GRE 10 +VALUE Tunnel-Type DVS 11 +VALUE Tunnel-Type IP-in-IP 12 + +# Tunnel Medium Type + +VALUE Tunnel-Medium-Type IP 1 +VALUE Tunnel-Medium-Type X25 2 +VALUE Tunnel-Medium-Type ATM 3 +VALUE Tunnel-Medium-Type Frame-Relay 4 +VALUE Tunnel-Medium-Type BBN-1822 5 +VALUE Tunnel-Medium-Type IEEE-802 6 +VALUE Tunnel-Medium-Type E.163 7 +VALUE Tunnel-Medium-Type E.164 8 +VALUE Tunnel-Medium-Type F.69 9 +VALUE Tunnel-Medium-Type X.121 10 +VALUE Tunnel-Medium-Type IPX 11 +VALUE Tunnel-Medium-Type Appletalk 12 +VALUE Tunnel-Medium-Type DecNet-IV 13 +VALUE Tunnel-Medium-Type Banyan-Vines 14 +VALUE Tunnel-Medium-Type E.164-NSAP 15 diff --git a/include/freeradius-client.h b/include/freeradius-client.h index 797cdfaa..727af77a 100644 --- a/include/freeradius-client.h +++ b/include/freeradius-client.h @@ -369,11 +369,27 @@ typedef struct rc_conf rc_handle; /* Server data structures */ +typedef struct attr_flags +{ + char has_tag; /* attribute allows tags */ + signed char tag; + uint8_t encrypt; /* encryption method */ +} ATTR_FLAGS; + +/* + * Values of the encryption flags. + */ +#define FLAG_ENCRYPT_NONE (0) +#define FLAG_ENCRYPT_USER_PASSWORD (1) +#define FLAG_ENCRYPT_TUNNEL_PASSWORD (2) +#define FLAG_ENCRYPT_ASCEND_SECRET (3) + typedef struct dict_attr { char name[NAME_LENGTH + 1]; //!< attribute name. int value; //!< attribute index. int type; //!< string, int, etc.. + ATTR_FLAGS flags; struct dict_attr *next; } DICT_ATTR; @@ -399,6 +415,7 @@ typedef struct value_pair int type; uint32_t lvalue; char strvalue[AUTH_STRING_LEN + 1]; + ATTR_FLAGS flags; struct value_pair *next; } VALUE_PAIR; @@ -463,6 +480,8 @@ int rc_avpair_tostr(rc_handle const *, VALUE_PAIR *, char *, int, char *, int); char *rc_avpair_log(rc_handle const *, VALUE_PAIR *, char *buf, size_t buf_len); VALUE_PAIR *rc_avpair_readin(rc_handle const *, FILE *); +int rc_tunnel_pwdecode(uint8_t *, int *, const char *, const char *); + /* buildreq.c */ void rc_buildreq(rc_handle const *, SEND_DATA *, int, char *, unsigned short, char *, int, int); diff --git a/lib/avpair.c b/lib/avpair.c index 8c2acb9e..002ea534 100644 --- a/lib/avpair.c +++ b/lib/avpair.c @@ -112,6 +112,7 @@ VALUE_PAIR *rc_avpair_new (rc_handle const *rh, int attrid, void const *pval, in { VALUE_PAIR *vp = NULL; DICT_ATTR *pda; + int attrType; attrid = attrid | (vendorpec << 16); if ((pda = rc_dict_getattr (rh, attrid)) == NULL) @@ -119,6 +120,8 @@ VALUE_PAIR *rc_avpair_new (rc_handle const *rh, int attrid, void const *pval, in rc_log(LOG_ERR,"rc_avpair_new: unknown attribute %d", attrid); return NULL; } + attrType = pda->type; + if (vendorpec != 0 && rc_dict_getvend(rh, vendorpec) == NULL) { rc_log(LOG_ERR,"rc_avpair_new: unknown Vendor-Id %d", vendorpec); @@ -126,10 +129,14 @@ VALUE_PAIR *rc_avpair_new (rc_handle const *rh, int attrid, void const *pval, in } if ((vp = malloc (sizeof (VALUE_PAIR))) != NULL) { - strlcpy (vp->name, pda->name, sizeof (vp->name)); + if (pda) + strlcpy (vp->name, pda->name, sizeof (vp->name)); + else + sprintf(vp->name, "attr%d", attrid); + vp->attribute = attrid; vp->next = NULL; - vp->type = pda->type; + vp->type = attrType; if (rc_avpair_assign (vp, pval, len) == 0) { /* XXX: Fix up Digest-Attributes */ @@ -272,9 +279,23 @@ VALUE_PAIR *rc_avpair_gen(rc_handle const *rh, VALUE_PAIR *pair, unsigned char c strcpy(pair->name, attr->name); pair->attribute = attr->value; pair->type = attr->type; + pair->flags = attr->flags; + + /* Handle attributes with tags. */ + if (attr->flags.has_tag) + { + pair->flags.tag = ptr[0]; + } switch (attr->type) { case PW_TYPE_STRING: + /* We don't do decryption here - we don't have all the + * info. */ + if (attr->flags.has_tag) + { + ++ptr; + --attrlen; + } memcpy(pair->strvalue, (char *)ptr, (size_t)attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; @@ -293,6 +314,11 @@ VALUE_PAIR *rc_avpair_gen(rc_handle const *rh, VALUE_PAIR *pair, unsigned char c goto skipit; } memcpy((char *)&lvalue, (char *)ptr, 4); + if (attr->flags.has_tag) + { + /* suppress the tag */ + lvalue &= 0xFFF; + } pair->lvalue = ntohl(lvalue); break; case PW_TYPE_IPV6ADDR: @@ -866,3 +892,114 @@ VALUE_PAIR *rc_avpair_readin(rc_handle const *rh, FILE *input) return vp; } + +/* + * Decode Tunnel-Password encrypted attributes. + * + * Defined in RFC-2868, this uses a two char SALT along with the + * initial intermediate value, to differentiate it from the + * above. + */ +int rc_tunnel_pwdecode(uint8_t *passwd, int *pwlen, const char *secret, + const char *vector) +{ + uint8_t buffer[AUTH_VECTOR_LEN + MAX_STRING_LEN + 3]; + uint8_t digest[AUTH_VECTOR_LEN]; + uint8_t decrypted[MAX_STRING_LEN + 1]; + int secretlen; + unsigned i, n, len; + + len = *pwlen; + + /* + * We need at least a salt. + */ + if (len < 2) { + rc_log(LOG_ERR, "tunnel password is too short"); + return -1; + } + + /* + * There's a salt, but no password. Or, there's a salt + * and a 'data_len' octet. It's wrong, but at least we + * can figure out what it means: the password is empty. + * + * Note that this means we ignore the 'data_len' field, + * if the attribute length tells us that there's no + * more data. So the 'data_len' field may be wrong, + * but that's ok... + */ + if (len <= 3) { + passwd[0] = 0; + *pwlen = 0; + return 0; + } + + len -= 2; /* discount the salt */ + + /* + * Use the secret to setup the decryption digest + */ + secretlen = strlen(secret); + + /* + * Set up the initial key: + * + * b(1) = MD5(secret + vector + salt) + */ + memcpy(buffer, secret, secretlen); + memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN); + memcpy(buffer + secretlen + AUTH_VECTOR_LEN, passwd, 2); + rc_md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN + 2); + + /* + * A quick check: decrypt the first octet of the password, + * which is the 'data_len' field. Ensure it's sane. + * + * 'n' doesn't include the 'data_len' octet + * 'len' does. + */ + n = passwd[2] ^ digest[0]; + if (n >= len) { + rc_log(LOG_ERR, "tunnel password is \ + too long for the attribute"); + return -1; + } + + /* + * Loop over the data, decrypting it, and generating + * the key for the next round of decryption. + */ + for (n = 0; n < len; n += AUTH_PASS_LEN) { + for (i = 0; i < AUTH_PASS_LEN; i++) { + decrypted[n + i] = passwd[n + i + 2] ^ digest[i]; + + /* + * Encrypted password may not be aligned + * on 16 octets, so we catch that here... + */ + if ((n + i) == len) break; + } + + /* + * Update the digest, based on + * + * b(n) = MD5(secret + cleartext(n-1) + * + * but only if there's more data... + */ + memcpy(buffer + secretlen, passwd + n + 2, AUTH_PASS_LEN); + rc_md5_calc(digest, buffer, secretlen + AUTH_PASS_LEN); + } + + /* + * We've already validated the length of the decrypted + * password. Copy it back to the caller. + */ + memcpy(passwd, decrypted + 1, decrypted[0]); + passwd[decrypted[0]] = 0; + *pwlen = decrypted[0]; + + return decrypted[0]; +} + diff --git a/lib/dict.c b/lib/dict.c index f1eae95e..d7ba0325 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -72,6 +72,8 @@ int rc_read_dictionary (rc_handle *rh, char const *filename) *cp = '\0'; } + ATTR_FLAGS flags; + memset(&flags, 0, sizeof(flags)); if (strncmp (buffer, "ATTRIBUTE", 9) == 0) { optstr[0] = '\0'; @@ -149,6 +151,7 @@ int rc_read_dictionary (rc_handle *rh, char const *filename) cp++; } if (strncmp(cp1, "vendor=", 7) == 0) + { cp1 += 7; dvend = rc_dict_findvend(rh, cp1); if (dvend == NULL) { @@ -157,7 +160,49 @@ int rc_read_dictionary (rc_handle *rh, char const *filename) cp1, line_no, filename); fclose(dictfd); return -1; + } } + else if(strcmp(cp1, "has_tag") == 0 || + strcmp(cp1, "has_tag=1") == 0) + { + flags.has_tag = 1; + } + else if (strncmp(cp1, "encrypt=", 8) == 0) + { + /* Encryption method, defaults to 0 (none). + Currently valid is just type 2, + Tunnel-Password style, which can only + be applied to strings. */ + char *pc = NULL; + flags.encrypt = strtol(cp1 + 8, &pc, 0); + if (*pc) { + rc_log(LOG_ERR, "%s: invalid option %s at line %d", + __FUNCTION__, cp1, line_no); + return -1; + } + } + } + } + /* + * Special checks for tags, they make our life much more + * difficult. + */ + + if (flags.has_tag) { + /* + * Only string, octets, and integer can be tagged. + */ + switch (type) { + case PW_TYPE_STRING: + case PW_TYPE_INTEGER: + break; + + default: + rc_log(LOG_ERR, "%s: Attributes of type %d cannot" + " be tagged (line %d).", + __FUNCTION__, type, line_no); + return -1; + } } @@ -171,6 +216,7 @@ int rc_read_dictionary (rc_handle *rh, char const *filename) strcpy (attr->name, namestr); attr->value = value | (attr_vendorspec << 16); attr->type = type; + attr->flags = flags; if (dvend != NULL) { attr->value = value | (dvend->vendorpec << 16);