update otp_hotp() to support 6,7,8,9 digit otp's
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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        <freeradius-devel/autoconf.h>
45
46 #include        <stdio.h>
47 #include        <stdlib.h>
48 #include        <string.h>
49 #include        <ctype.h>
50
51 #include        <freeradius-devel/radiusd.h>
52 #include        <freeradius-devel/modules.h>
53
54 #include        <freeradius-devel/md4.h>
55 #include        <freeradius-devel/md5.h>
56 #include        <freeradius-devel/sha1.h>
57
58
59 #define         SM_AUTHTYPE     ((11406<<16)|101)
60 #define         SM_CHALLENGE    ((11406<<16)|102)
61 #define         SM_RESPONSE     ((11406<<16)|103)
62
63
64
65
66 static void calc_apop_digest(char * buffer, const char * challenge, int challen, const char * password){
67         MD5_CTX Context;
68
69         MD5Init(&Context);
70         MD5Update(&Context,challenge,challen);
71         MD5Update(&Context,password,strlen(password));
72         MD5Final(buffer,&Context);
73 }
74
75
76 static void calc_md5_digest(char * buffer, const char * challenge, int challen, const char * password){
77         char buf[1024];
78         int i;
79         MD5_CTX Context;
80
81         memset(buf, 0, 1024);
82         memset(buf, 0x36, 64);
83         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
84         memcpy(buf+64, challenge, challen);
85         MD5Init(&Context);
86         MD5Update(&Context,buf,64+challen);
87         memset(buf, 0x5c, 64);
88         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
89         MD5Final(buf+64,&Context);
90         MD5Init(&Context);
91         MD5Update(&Context,buf,64+16);
92         MD5Final(buffer,&Context);
93 }
94
95 static void calc_md4_digest(char * buffer, const char * challenge, int challen, const char * password){
96         char buf[1024];
97         int i;
98         MD4_CTX Context;
99
100         memset(buf, 0, 1024);
101         memset(buf, 0x36, 64);
102         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
103         memcpy(buf+64, challenge, challen);
104         MD4Init(&Context);
105         MD4Update(&Context,buf,64+challen);
106         memset(buf, 0x5c, 64);
107         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
108         MD4Final(buf+64,&Context);
109         MD4Init(&Context);
110         MD4Update(&Context,buf,64+16);
111         MD4Final(buffer,&Context);
112 }
113
114 static void calc_sha1_digest(char * buffer, const char * challenge, int challen, const char * password){
115         char buf[1024];
116         int i;
117         SHA1_CTX Context;
118
119         memset(buf, 0, 1024);
120         memset(buf, 0x36, 64);
121         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
122         memcpy(buf+64, challenge, challen);
123         SHA1Init(&Context);
124         SHA1Update(&Context,buf,64+challen);
125         memset(buf, 0x5c, 64);
126         for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
127         SHA1Final(buf+64,&Context);
128         SHA1Init(&Context);
129         SHA1Update(&Context,buf,64+20);
130         SHA1Final(buffer,&Context);
131 }
132
133
134 static int cram_authenticate(UNUSED void * instance, REQUEST *request)
135 {
136         VALUE_PAIR *authtype, *challenge, *response, *password;
137         char buffer[64];
138
139         password = pairfind(request->config_items, PW_PASSWORD);
140         if(!password) {
141                 radlog(L_AUTH, "rlm_cram: Password is not configured for user");
142                 return RLM_MODULE_INVALID;
143         }
144         authtype = pairfind(request->packet->vps, SM_AUTHTYPE);
145         if(!authtype) {
146                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Authtype missed");
147                 return RLM_MODULE_INVALID;
148         }
149         challenge = pairfind(request->packet->vps, SM_CHALLENGE);
150         if(!challenge) {
151                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Challenge missed");
152                 return RLM_MODULE_INVALID;
153         }
154         response = pairfind(request->packet->vps, SM_RESPONSE);
155         if(!response) {
156                 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Response missed");
157                 return RLM_MODULE_INVALID;
158         }
159         switch(authtype->lvalue){
160                 case 2:                         /*      CRAM-MD5        */
161                         if(challenge->length < 5 || response->length != 16) {
162                                 radlog(L_AUTH, "rlm_cram: invalid MD5 challenge/response length");
163                                 return RLM_MODULE_INVALID;
164                         }
165                         calc_md5_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
166                         if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
167                         break;
168                 case 3:                         /*      APOP    */
169                         if(challenge->length < 5 || response->length != 16) {
170                                 radlog(L_AUTH, "rlm_cram: invalid APOP challenge/response length");
171                                 return RLM_MODULE_INVALID;
172                         }
173                         calc_apop_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
174                         if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
175                         break;
176                 case 8:                         /*      CRAM-MD4        */
177                         if(challenge->length < 5 || response->length != 16) {
178                                 radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
179                                 return RLM_MODULE_INVALID;
180                         }
181                         calc_md4_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
182                         if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
183                         break;
184                 case 9:                         /*      CRAM-SHA1       */
185                         if(challenge->length < 5 || response->length != 20) {
186                                 radlog(L_AUTH, "rlm_cram: invalid MD4 challenge/response length");
187                                 return RLM_MODULE_INVALID;
188                         }
189                         calc_sha1_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
190                         if(!memcmp(buffer, response->vp_strvalue, 20)) return RLM_MODULE_OK;
191                         break;
192                 default:
193                         radlog(L_AUTH, "rlm_cram: unsupported Sandy-Mail-Authtype");
194                         return RLM_MODULE_INVALID;
195         }
196         return RLM_MODULE_NOTFOUND;
197
198 }
199
200 module_t rlm_cram = {
201         RLM_MODULE_INIT,
202         "CRAM",
203         RLM_TYPE_THREAD_SAFE,           /* type */
204         NULL,                           /* instantiation */
205         NULL,                           /* detach */
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 };