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.
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.
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
20 * Copyright 2002 The FreeRADIUS server project
24 * CRAM mail authentication (APOP, CRAM-MD5)
27 * rlm_cram module is a part of Mail authorization/authentication
30 * Attributes used (Vendor Code/PEN: 11406, you may change it to your own)
31 * 101 (Sandy-Mail-Authtype), selects CRAM protocol, possible values:
36 * 102 (Sandy-Mail-Challenge), contains server's challenge (usually
38 * 103 (Sandy-Mail-Response), contains client's response, 16 octets
39 * for APOP/CRAM-MD5/CRAM-MD4, 20 octets for CRAM-SHA1
41 * (c) 2002 by SANDY (http://www.sandy.ru/) under GPL
44 #include <freeradius-devel/autoconf.h>
51 #include <freeradius-devel/radiusd.h>
52 #include <freeradius-devel/modules.h>
54 #include <freeradius-devel/md4.h>
55 #include <freeradius-devel/md5.h>
56 #include <freeradius-devel/sha1.h>
59 #define SM_AUTHTYPE ((11406<<16)|101)
60 #define SM_CHALLENGE ((11406<<16)|102)
61 #define SM_RESPONSE ((11406<<16)|103)
66 static void calc_apop_digest(char * buffer, const char * challenge, int challen, const char * password){
70 MD5Update(&Context,challenge,challen);
71 MD5Update(&Context,password,strlen(password));
72 MD5Final(buffer,&Context);
76 static void calc_md5_digest(char * buffer, const char * challenge, int challen, const char * password){
82 memset(buf, 0x36, 64);
83 for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
84 memcpy(buf+64, challenge, challen);
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);
91 MD5Update(&Context,buf,64+16);
92 MD5Final(buffer,&Context);
95 static void calc_md4_digest(char * buffer, const char * challenge, int challen, const char * password){
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);
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);
110 MD4Update(&Context,buf,64+16);
111 MD4Final(buffer,&Context);
114 static void calc_sha1_digest(char * buffer, const char * challenge, int challen, const char * password){
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);
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);
129 SHA1Update(&Context,buf,64+20);
130 SHA1Final(buffer,&Context);
134 static int cram_authenticate(UNUSED void * instance, REQUEST *request)
136 VALUE_PAIR *authtype, *challenge, *response, *password;
139 password = pairfind(request->config_items, PW_PASSWORD);
141 radlog(L_AUTH, "rlm_cram: Password is not configured for user");
142 return RLM_MODULE_INVALID;
144 authtype = pairfind(request->packet->vps, SM_AUTHTYPE);
146 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Authtype missed");
147 return RLM_MODULE_INVALID;
149 challenge = pairfind(request->packet->vps, SM_CHALLENGE);
151 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Challenge missed");
152 return RLM_MODULE_INVALID;
154 response = pairfind(request->packet->vps, SM_RESPONSE);
156 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Response missed");
157 return RLM_MODULE_INVALID;
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;
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;
169 if(challenge->length < 5 || response->length != 16) {
170 radlog(L_AUTH, "rlm_cram: invalid APOP challenge/response length");
171 return RLM_MODULE_INVALID;
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;
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;
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;
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;
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;
193 radlog(L_AUTH, "rlm_cram: unsupported Sandy-Mail-Authtype");
194 return RLM_MODULE_INVALID;
196 return RLM_MODULE_NOTFOUND;
200 module_t rlm_cram = {
203 RLM_TYPE_THREAD_SAFE, /* type */
204 NULL, /* instantiation */
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 */