radsecproxy-1.6.5.
[radsecproxy.git] / radmsg.c
index 26f8fbe..7f6dd9d 100644 (file)
--- a/radmsg.c
+++ b/radmsg.c
@@ -6,7 +6,11 @@
  * copyright notice and this permission notice appear in all copies.
  */
 
+#ifdef SYS_SOLARIS9
+#include <sys/inttypes.h>
+#else
 #include <stdint.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 #include <arpa/inet.h>
@@ -16,6 +20,7 @@
 #include "debug.h"
 #include <pthread.h>
 #include <openssl/hmac.h>
+#include <openssl/rand.h>
 
 #define RADLEN(x) ntohs(((uint16_t *)(x))[1])
 
@@ -28,18 +33,24 @@ void radmsg_free(struct radmsg *msg) {
 
 struct radmsg *radmsg_init(uint8_t code, uint8_t id, uint8_t *auth) {
     struct radmsg *msg;
-    
+
     msg = malloc(sizeof(struct radmsg));
     if (!msg)
         return NULL;
+    memset(msg, 0, sizeof(struct radmsg));
     msg->attrs = list_create();
     if (!msg->attrs) {
        free(msg);
         return NULL;
-    }  
+    }
     msg->code = code;
     msg->id = id;
-    memcpy(msg->auth, auth, 16);
+    if (auth)
+       memcpy(msg->auth, auth, 16);
+    else if (!RAND_bytes(msg->auth, 16)) {
+       free(msg);
+       return NULL;
+    }
     return msg;
 }
 
@@ -51,6 +62,30 @@ int radmsg_add(struct radmsg *msg, struct tlv *attr) {
     return list_push(msg->attrs, attr);
 }
 
+/** Return a new list with all tlv's in \a msg of type \a type. The
+ * caller is responsible for freeing the list by calling \a
+ * list_free(). */
+struct list *
+radmsg_getalltype(const struct radmsg *msg, uint8_t type)
+{
+    struct list *list = NULL;
+    struct list_node *node = NULL;
+
+    if (!msg || !msg->attrs)
+        return NULL;
+    list = list_create();
+    if (!list)
+        return NULL;
+
+    for (node = list_first(msg->attrs); node; node = list_next(node))
+        if (((struct tlv *) node->data)->t == type)
+            if (list_push(list, node->data) != 1) {
+                list_free(list);
+                return NULL;
+            }
+    return list;
+}
+
 /* returns first tlv of the given type */
 struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
     struct list_node *node;
@@ -66,38 +101,29 @@ struct tlv *radmsg_gettype(struct radmsg *msg, uint8_t type) {
     return NULL;
 }
 
-uint8_t *radmsg2buf(struct radmsg *msg) {
-    struct list_node *node;
-    struct tlv *tlv;
-    int size;
-    uint8_t *buf, *p;
-
-    if (!msg || !msg->attrs)
-        return NULL;
-    size = 20;
-    for (node = list_first(msg->attrs); node; node = list_next(node))
-        size += 2 + ((struct tlv *)node->data)->l;
-    if (size > 65535)
-        return NULL;
-    buf = malloc(size);
-    if (!buf)
-        return NULL;
-    
-    p = buf;
-    *p++ = msg->code;
-    *p++ = msg->id;
-    *(uint16_t *)p = htons(size);
-    p += 2;
-    memcpy(p, msg->auth, 16);
-    p += 16;
+/** Copy all attributes of type \a type from \a src to \a dst.
+ *
+ * If all attributes were copied successfully, the number of
+ * attributes copied is returned.
+ *
+ * If copying failed, a negative number is returned. */
+int radmsg_copy_attrs(struct radmsg *dst,
+                      const struct radmsg *src,
+                      uint8_t type)
+{
+    struct list_node *node = NULL;
+    struct list *list = radmsg_getalltype(src, type);
+    int n = 0;
 
-    for (node = list_first(msg->attrs); node; node = list_next(node)) {
-        tlv = (struct tlv *)node->data;
-        p = tlv2buf(p, tlv);
-       p[-1] += 2;
-        p += tlv->l;
+    for (node = list_first(list); node; node = list_next(node)) {
+        if (radmsg_add(dst, copytlv((struct tlv *) node->data)) != 1) {
+            n = -1;
+            break;
+        }
+        n++;
     }
-    return buf;
+    list_free(list);
+    return n;
 }
 
 int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
@@ -106,7 +132,7 @@ int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
     static HMAC_CTX hmacctx;
     unsigned int md_len;
     uint8_t auth[16], hash[EVP_MAX_MD_SIZE];
-    
+
     pthread_mutex_lock(&lock);
     if (first) {
        HMAC_CTX_init(&hmacctx);
@@ -130,8 +156,8 @@ int _checkmsgauth(unsigned char *rad, uint8_t *authattr, uint8_t *secret) {
        debug(DBG_WARN, "message authenticator, wrong value");
        pthread_mutex_unlock(&lock);
        return 0;
-    }  
-       
+    }
+
     pthread_mutex_unlock(&lock);
     return 1;
 }
@@ -143,7 +169,7 @@ int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
     unsigned char hash[EVP_MAX_MD_SIZE];
     unsigned int len;
     int result;
-    
+
     pthread_mutex_lock(&lock);
     if (first) {
        EVP_MD_CTX_init(&mdctx);
@@ -151,7 +177,7 @@ int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
     }
 
     len = RADLEN(rad);
-    
+
     result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
              EVP_DigestUpdate(&mdctx, rad, 4) &&
              EVP_DigestUpdate(&mdctx, reqauth, 16) &&
@@ -163,14 +189,113 @@ int _validauth(unsigned char *rad, unsigned char *reqauth, unsigned char *sec) {
     pthread_mutex_unlock(&lock);
     return result;
 }
-             
+
+int _createmessageauth(unsigned char *rad, unsigned char *authattrval, uint8_t *secret) {
+    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+    static unsigned char first = 1;
+    static HMAC_CTX hmacctx;
+    unsigned int md_len;
+
+    if (!authattrval)
+       return 1;
+
+    pthread_mutex_lock(&lock);
+    if (first) {
+       HMAC_CTX_init(&hmacctx);
+       first = 0;
+    }
+
+    memset(authattrval, 0, 16);
+    md_len = 0;
+    HMAC_Init_ex(&hmacctx, secret, strlen((char *)secret), EVP_md5(), NULL);
+    HMAC_Update(&hmacctx, rad, RADLEN(rad));
+    HMAC_Final(&hmacctx, authattrval, &md_len);
+    if (md_len != 16) {
+       debug(DBG_WARN, "message auth computation failed");
+       pthread_mutex_unlock(&lock);
+       return 0;
+    }
+    pthread_mutex_unlock(&lock);
+    return 1;
+}
+
+int _radsign(unsigned char *rad, unsigned char *sec) {
+    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+    static unsigned char first = 1;
+    static EVP_MD_CTX mdctx;
+    unsigned int md_len;
+    int result;
+
+    pthread_mutex_lock(&lock);
+    if (first) {
+       EVP_MD_CTX_init(&mdctx);
+       first = 0;
+    }
+
+    result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
+             EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
+             EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
+             EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
+             md_len == 16);
+    pthread_mutex_unlock(&lock);
+    return result;
+}
+
+uint8_t *radmsg2buf(struct radmsg *msg, uint8_t *secret) {
+    struct list_node *node;
+    struct tlv *tlv;
+    int size;
+    uint8_t *buf, *p, *msgauth = NULL;
+
+    if (!msg || !msg->attrs)
+        return NULL;
+    size = 20;
+    for (node = list_first(msg->attrs); node; node = list_next(node))
+        size += 2 + ((struct tlv *)node->data)->l;
+    if (size > 65535)
+        return NULL;
+    buf = malloc(size);
+    if (!buf)
+        return NULL;
+
+    p = buf;
+    *p++ = msg->code;
+    *p++ = msg->id;
+    *(uint16_t *)p = htons(size);
+    p += 2;
+    memcpy(p, msg->auth, 16);
+    p += 16;
+
+    for (node = list_first(msg->attrs); node; node = list_next(node)) {
+        tlv = (struct tlv *)node->data;
+        p = tlv2buf(p, tlv);
+       p[-1] += 2;
+       if (tlv->t == RAD_Attr_Message_Authenticator && secret)
+           msgauth = p;
+        p += tlv->l;
+    }
+    if (msgauth && !_createmessageauth(buf, msgauth, secret)) {
+       free(buf);
+       return NULL;
+    }
+    if (secret) {
+       if ((msg->code == RAD_Access_Accept || msg->code == RAD_Access_Reject || msg->code == RAD_Access_Challenge || msg->code == RAD_Accounting_Response || msg->code == RAD_Accounting_Request) && !_radsign(buf, secret)) {
+           free(buf);
+           return NULL;
+       }
+       if (msg->code == RAD_Accounting_Request)
+           memcpy(msg->auth, buf + 4, 16);
+    }
+    return buf;
+}
+
 /* if secret set we also validate message authenticator if present */
 struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
     struct radmsg *msg;
-    uint8_t t, l, *v, *p, auth[16];
+    uint8_t t, l, *v = NULL, *p, auth[16];
     uint16_t len;
     struct tlv *attr;
-    
+
     len = RADLEN(buf);
     if (len < 20)
        return NULL;
@@ -187,7 +312,7 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
        debug(DBG_WARN, "buf2radmsg: Invalid auth, ignoring reply");
        return NULL;
     }
-       
+
     msg = radmsg_init(buf[0], buf[1], (uint8_t *)buf + 4);
     if (!msg)
         return NULL;
@@ -211,7 +336,7 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
             v = p;
             p += l;
         }
-       
+
        if (t == RAD_Attr_Message_Authenticator && secret) {
            if (rqauth)
                memcpy(buf + 4, rqauth, 16);
@@ -236,3 +361,7 @@ struct radmsg *buf2radmsg(uint8_t *buf, uint8_t *secret, uint8_t *rqauth) {
     }
     return msg;
 }
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */