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,2006 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/ident.h>
47 #include <freeradius-devel/autoconf.h>
54 #include <freeradius-devel/radiusd.h>
55 #include <freeradius-devel/modules.h>
57 #include <freeradius-devel/md5.h>
60 #define SM_AUTHTYPE ((11406<<16)|101)
61 #define SM_CHALLENGE ((11406<<16)|102)
62 #define SM_RESPONSE ((11406<<16)|103)
67 static void calc_apop_digest(char * buffer, const char * challenge, int challen, const char * password){
71 MD5Update(&Context,challenge,challen);
72 MD5Update(&Context,password,strlen(password));
73 MD5Final(buffer,&Context);
77 static void calc_md5_digest(char * buffer, const char * challenge, int challen, const char * password){
83 memset(buf, 0x36, 64);
84 for(i=0; i<64 && password[i]; i++) buf[i]^=password[i];
85 memcpy(buf+64, challenge, challen);
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);
92 MD5Update(&Context,buf,64+16);
93 MD5Final(buffer,&Context);
96 static void calc_md4_digest(char * buffer, const char * challenge, int challen, const char * password){
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);
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);
111 MD4Update(&Context,buf,64+16);
112 MD4Final(buffer,&Context);
115 static void calc_sha1_digest(char * buffer, const char * challenge, int challen, const char * password){
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);
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);
130 SHA1Update(&Context,buf,64+20);
131 SHA1Final(buffer,&Context);
135 static int cram_authenticate(UNUSED void * instance, REQUEST *request)
137 VALUE_PAIR *authtype, *challenge, *response, *password;
140 password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD);
142 radlog(L_AUTH, "rlm_cram: Cleartext-Password is required for authentication.");
143 return RLM_MODULE_INVALID;
145 authtype = pairfind(request->packet->vps, SM_AUTHTYPE);
147 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Authtype missed");
148 return RLM_MODULE_INVALID;
150 challenge = pairfind(request->packet->vps, SM_CHALLENGE);
152 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Challenge missed");
153 return RLM_MODULE_INVALID;
155 response = pairfind(request->packet->vps, SM_RESPONSE);
157 radlog(L_AUTH, "rlm_cram: Required attribute Sandy-Mail-Response missed");
158 return RLM_MODULE_INVALID;
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;
166 calc_md5_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
167 if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
170 if(challenge->length < 5 || response->length != 16) {
171 radlog(L_AUTH, "rlm_cram: invalid APOP challenge/response length");
172 return RLM_MODULE_INVALID;
174 calc_apop_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
175 if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
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;
182 calc_md4_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
183 if(!memcmp(buffer, response->vp_strvalue, 16)) return RLM_MODULE_OK;
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;
190 calc_sha1_digest(buffer, challenge->vp_strvalue, challenge->length, password->vp_strvalue);
191 if(!memcmp(buffer, response->vp_strvalue, 20)) return RLM_MODULE_OK;
194 radlog(L_AUTH, "rlm_cram: unsupported Sandy-Mail-Authtype");
195 return RLM_MODULE_INVALID;
197 return RLM_MODULE_NOTFOUND;
201 module_t rlm_cram = {
204 RLM_TYPE_THREAD_SAFE, /* type */
205 NULL, /* instantiation */
208 cram_authenticate, /* authenticate */
209 NULL, /* authorize */
210 NULL, /* pre-accounting */
211 NULL, /* accounting */
212 NULL, /* checksimul */
213 NULL, /* pre-proxy */
214 NULL, /* post-proxy */