6f3816262bd0ed9f4ba21395d3af3cb68416484a
[freeradius.git] / src / modules / rlm_cram / rlm_cram.c
1 /*
2  * rlm_cram.c   
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2002  The FreeRADIUS server project
21  */
22  
23 /*
24  *   CRAM mail authentication (APOP, CRAM-MD5)
25  *   by 3APA3A
26  *
27  *   rlm_cram module is a part of Mail authorization/authentication
28  *   support.
29  *
30  *   Attributes used (Vendor Code/PEN: 11406, you may change it to your own)
31  *      101 (Sandy-Mail-Authtype), selects CRAM protocol, possible values:
32  *              2: CRAM-MD5
33  *              3: APOP
34  *              8: CRAM-MD4
35  *              9: CRAM-SHA1
36  *      102 (Sandy-Mail-Challenge), contains server's challenge (usually
37  *      text banner)
38  *      103 (Sandy-Mail-Response), contains client's response, 16 octets
39  *      for APOP/CRAM-MD5/CRAM-MD4, 20 octets for CRAM-SHA1
40  *
41  *   (c) 2002 by SANDY (http://www.sandy.ru/) under GPL
42  */ 
43
44 #include        "autoconf.h"
45 #include        "libradius.h"
46
47 #include        <stdio.h>
48 #include        <stdlib.h>
49 #include        <string.h>
50 #include        <ctype.h>
51
52 #include        "radiusd.h"
53 #include        "modules.h"
54
55 #include        "md4.h"
56 #include        "md5.h"
57 #include        "sha1.h"
58
59
60 #define         SM_AUTHTYPE     ((11406<<16)|101)
61 #define         SM_CHALLENGE    ((11406<<16)|102)
62 #define         SM_RESPONSE     ((11406<<16)|103)
63
64
65
66
67 static void calc_apop_digest(char * buffer, const char * challenge, int challen, const char * password){
68         MD5_CTX Context;
69
70         MD5Init(&Context);
71         MD5Update(&Context,challenge,challen);
72         MD5Update(&Context,password,strlen(password));
73         MD5Final(buffer,&Context);
74 }
75
76
77 static void calc_md5_digest(char * buffer, const char * challenge, int challen, const char * password){
78         char buf[1024];
79         int i;
80         MD5_CTX Context;
81
82         memset(buf, 0, 1024);
83         memset(buf, 0x36, 64);
84         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
85         memcpy(buf+64, challenge, challen);
86         MD5Init(&Context);
87         MD5Update(&Context,buf,64+challen);
88         memset(buf, 0x5c, 64);
89         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
90         MD5Final(buf+64,&Context);
91         MD5Init(&Context);
92         MD5Update(&Context,buf,64+16);
93         MD5Final(buffer,&Context);
94 }
95
96 static void calc_md4_digest(char * buffer, const char * challenge, int challen, const char * password){
97         char buf[1024];
98         int i;
99         MD4_CTX Context;
100
101         memset(buf, 0, 1024);
102         memset(buf, 0x36, 64);
103         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
104         memcpy(buf+64, challenge, challen);
105         MD4Init(&Context);
106         MD4Update(&Context,buf,64+challen);
107         memset(buf, 0x5c, 64);
108         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
109         MD4Final(buf+64,&Context);
110         MD4Init(&Context);
111         MD4Update(&Context,buf,64+16);
112         MD4Final(buffer,&Context);
113 }
114
115 static void calc_sha1_digest(char * buffer, const char * challenge, int challen, const char * password){
116         char buf[1024];
117         int i;
118         SHA1_CTX Context;
119
120         memset(buf, 0, 1024);
121         memset(buf, 0x36, 64);
122         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
123         memcpy(buf+64, challenge, challen);
124         SHA1Init(&Context);
125         SHA1Update(&Context,buf,64+challen);
126         memset(buf, 0x5c, 64);
127         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
128         SHA1Final(buf+64,&Context);
129         SHA1Init(&Context);
130         SHA1Update(&Context,buf,64+20);
131         SHA1Final(buffer,&Context);
132 }
133
134
135 static int cram_authenticate(void * instance, REQUEST *request)
136 {
137         VALUE_PAIR *authtype, *challenge, *response, *password;
138         char buffer[64];
139
140         password = pairfind(request->config_items, PW_PASSWORD);
141         if(!password) {
142                 radlog(L_AUTH, "rlm_cram: Password is not configured for user");
143                 return RLM_MODULE_INVALID;
144         }
145         authtype = pairfind(request->packet->vps, SM_AUTHTYPE);
146         if(!authtype) {
147                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Authtype missed");
148                 return RLM_MODULE_INVALID;
149         }
150         challenge = pairfind(request->packet->vps, SM_CHALLENGE);
151         if(!challenge) {
152                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Challenge missed");
153                 return RLM_MODULE_INVALID;
154         }
155         response = pairfind(request->packet->vps, SM_RESPONSE);
156         if(!response) {
157                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Response missed");
158                 return RLM_MODULE_INVALID;
159         }
160         switch(authtype->lvalue){
161                 case 2:                         /*      CRAM-MD5        */
162                         if(challenge->length < 5 || response->length != 16) {
163                                 radlog(L_AUTH, "rlm_cram: invalid MD5 challenge/response length");
164                                 return RLM_MODULE_INVALID;
165                         }
166                         calc_md5_digest(buffer, challenge->strvalue, challenge->length, password->strvalue);
167                         if(!memcmp(buffer, response->strvalue, 16)) return RLM_MODULE_OK;
168                         break;
169                 case 3:                         /*      APOP    */
170                         if(challenge->length < 5 || response->length != 16) {
171                                 radlog(L_AUTH, "rlm_cram: invalid APOP challenge/response length");
172                                 return RLM_MODULE_INVALID;
173                         }
174                         calc_apop_digest(buffer, challenge->strvalue, challenge->length, password->strvalue);
175                         if(!memcmp(buffer, response->strvalue, 16)) return RLM_MODULE_OK;
176                         break;
177                 case 8:                         /*      CRAM-MD4        */
178                         if(challenge->length < 5 || response->length != 16) {
179                                 radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
180                                 return RLM_MODULE_INVALID;
181                         }
182                         calc_md4_digest(buffer, challenge->strvalue, challenge->length, password->strvalue);
183                         if(!memcmp(buffer, response->strvalue, 16)) return RLM_MODULE_OK;
184                         break;
185                 case 9:                         /*      CRAM-SHA1       */
186                         if(challenge->length < 5 || response->length != 20) {
187                                 radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
188                                 return RLM_MODULE_INVALID;
189                         }
190                         calc_sha1_digest(buffer, challenge->strvalue, challenge->length, password->strvalue);
191                         if(!memcmp(buffer, response->strvalue, 20)) return RLM_MODULE_OK;
192                         break;
193                 default:
194                         radlog(L_AUTH, "rlm_cram: unsupported Sandy-Mail-Authtype");
195                         return RLM_MODULE_INVALID;
196         }
197         return RLM_MODULE_NOTFOUND;
198
199 }
200
201 module_t rlm_cram = {
202   "CRAM",
203   RLM_TYPE_THREAD_SAFE,                         /* type */
204   NULL,                         /* initialize */
205   NULL,         /* instantiation */
206   {
207           cram_authenticate,    /* authenticate */
208           NULL,                 /* authorize */
209           NULL,                 /* pre-accounting */
210           NULL,                 /* accounting */
211           NULL,                 /* checksimul */
212           NULL,                 /* pre-proxy */
213           NULL,                 /* post-proxy */
214           NULL                  /* post-auth */
215   },
216   NULL,                         /* detach */
217   NULL,                         /* destroy */
218 };