1 /* cram-md5.c implementation of SASL mechanism CRAM-MD5 from RFC 2195
2 * Copyright (C) 2002 Simon Josefsson
4 * This file is part of libgsasl.
6 * Libgsasl is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * Libgsasl 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with libgsasl; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 _gsasl_cram_md5_client_init (Gsasl_ctx
*ctx
)
33 if (gcry_check_version(GCRYPT_VERSION
) == NULL
)
34 return GSASL_GCRYPT_ERROR
;
36 res
= gcry_control (GCRYCTL_INIT_SECMEM
, 512, 0);
37 if (res
!= GCRYERR_SUCCESS
)
38 return GSASL_GCRYPT_ERROR
;
44 _gsasl_cram_md5_client_done (Gsasl_ctx
*ctx
)
50 _gsasl_cram_md5_client_start (Gsasl_session_ctx
*cctx
,
56 ctx
= gsasl_client_ctx_get (cctx
);
58 return GSASL_CANNOT_GET_CTX
;
60 if (gsasl_client_callback_authentication_id_get (ctx
) == NULL
)
61 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK
;
63 if (gsasl_client_callback_password_get (ctx
) == NULL
)
64 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK
;
66 done
= (int*) malloc(sizeof(*done
));
68 return GSASL_MALLOC_ERROR
;
78 _gsasl_cram_md5_client_step (Gsasl_session_ctx
*cctx
,
85 int *step
= mech_data
;
87 Gsasl_client_callback_authentication_id cb_authentication_id
;
88 Gsasl_client_callback_password cb_password
;
91 int hash_len
= gcry_md_get_algo_dlen (GCRY_MD_MD5
);
103 return GSASL_NEEDS_MORE
;
106 ctx
= gsasl_client_ctx_get (cctx
);
108 return GSASL_CANNOT_GET_CTX
;
110 cb_authentication_id
= gsasl_client_callback_authentication_id_get (ctx
);
111 if (cb_authentication_id
== NULL
)
112 return GSASL_NEED_CLIENT_AUTHENTICATION_ID_CALLBACK
;
114 cb_password
= gsasl_client_callback_password_get (ctx
);
115 if (cb_password
== NULL
)
116 return GSASL_NEED_CLIENT_PASSWORD_CALLBACK
;
118 md5h
= gcry_md_open (GCRY_MD_MD5
, GCRY_MD_FLAG_HMAC
);
120 return GSASL_GCRYPT_ERROR
;
122 /* XXX? password stored in callee's output buffer */
124 res
= cb_password (cctx
, output
, &len
);
127 tmp
= gsasl_utf8_nfkc_normalize (output
, len
);
129 return GSASL_UNICODE_NORMALIZATION_ERROR
;
130 res
= gcry_md_setkey (md5h
, tmp
, strlen(tmp
));
132 if (res
!= GCRYERR_SUCCESS
)
133 return GSASL_GCRYPT_ERROR
;
135 gcry_md_write (md5h
, input
, input_len
);
137 hash
= gcry_md_read (md5h
, GCRY_MD_MD5
);
139 return GSASL_GCRYPT_ERROR
;
142 res
= cb_authentication_id (cctx
, output
, &len
);
145 tmp
= gsasl_utf8_nfkc_normalize (output
, len
);
147 return GSASL_UNICODE_NORMALIZATION_ERROR
;
148 if (strlen(tmp
) + strlen(" ") + 2*hash_len
>= *output_len
)
151 return GSASL_TOO_SMALL_BUFFER
;
154 memcpy(output
, tmp
, len
);
158 for (i
= 0; i
< hash_len
; i
++)
160 output
[len
+ 2*i
+ 1] = HEXCHAR(hash
[i
]);
161 output
[len
+ 2*i
+ 0] = HEXCHAR(hash
[i
] >> 4);
163 *output_len
= len
+ 2*hash_len
;
169 return GSASL_NEEDS_MORE
;
173 _gsasl_cram_md5_client_finish (Gsasl_session_ctx
*cctx
,
176 int *step
= mech_data
;
186 _gsasl_cram_md5_server_init (Gsasl_ctx
*ctx
)
188 if (gcry_check_version(GCRYPT_VERSION
) == NULL
)
189 return GSASL_GCRYPT_ERROR
;
195 _gsasl_cram_md5_server_done (Gsasl_ctx
*ctx
)
201 _gsasl_cram_md5_server_start (Gsasl_session_ctx
*sctx
,
208 ctx
= gsasl_server_ctx_get (sctx
);
210 return GSASL_CANNOT_GET_CTX
;
212 if (gsasl_server_callback_cram_md5_get (ctx
) == NULL
&&
213 gsasl_server_callback_retrieve_get (ctx
) == NULL
)
214 return GSASL_NEED_SERVER_CRAM_MD5_CALLBACK
;
216 /* XXX this is ad-hoc and uses "localhost" instead of FQDN */
218 #define START_OF_XS 1
219 #define NUMBER_OF_XS 16
220 #define CHALLENGE_FORMAT "<XXXXXXXXXXXXXXXX.libgsasl@localhost>"
222 challenge
= (char*) malloc(strlen(CHALLENGE_FORMAT
) + 1);
223 if (challenge
== NULL
)
224 return GSASL_MALLOC_ERROR
;
226 strcpy(challenge
, CHALLENGE_FORMAT
);
228 gcry_randomize (challenge
+ 1, NUMBER_OF_XS
, GCRY_WEAK_RANDOM
);
230 for (i
= 0; i
< NUMBER_OF_XS
/2; i
++)
232 challenge
[START_OF_XS
+ NUMBER_OF_XS
/2 + i
] =
233 HEXCHAR(challenge
[START_OF_XS
+ i
]);
234 challenge
[START_OF_XS
+ i
] =
235 HEXCHAR(challenge
[START_OF_XS
+ i
] >> 4);
238 *mech_data
= challenge
;
244 _gsasl_cram_md5_server_step (Gsasl_session_ctx
*sctx
,
251 char *challenge
= mech_data
;
252 Gsasl_server_callback_cram_md5 cb_cram_md5
;
253 Gsasl_server_callback_retrieve cb_retrieve
;
254 int hash_len
= gcry_md_get_algo_dlen (GCRY_MD_MD5
);
255 char *username
= NULL
;
261 if (*output_len
< strlen(challenge
))
262 return GSASL_TOO_SMALL_BUFFER
;
264 *output_len
= strlen(challenge
);
265 memcpy(output
, challenge
, *output_len
);
267 return GSASL_NEEDS_MORE
;
270 if (input_len
<= hash_len
* 2)
271 return GSASL_MECHANISM_PARSE_ERROR
;
273 if (input
[input_len
- hash_len
* 2 - 1] != ' ')
274 return GSASL_MECHANISM_PARSE_ERROR
;
276 ctx
= gsasl_server_ctx_get (sctx
);
278 return GSASL_CANNOT_GET_CTX
;
280 cb_cram_md5
= gsasl_server_callback_cram_md5_get (ctx
);
281 cb_retrieve
= gsasl_server_callback_retrieve_get (ctx
);
282 if (cb_cram_md5
== NULL
&& cb_retrieve
== NULL
)
283 return GSASL_NEED_SERVER_CRAM_MD5_CALLBACK
;
285 username
= (char*) malloc(input_len
);
286 if (username
== NULL
)
287 return GSASL_MALLOC_ERROR
;
289 memcpy(username
, input
, input_len
- hash_len
* 2);
290 username
[input_len
- hash_len
* 2 - 1] = '\0';
296 response
= (char*) malloc(hash_len
* 2 + 1);
297 if (response
== NULL
)
300 return GSASL_MALLOC_ERROR
;
303 memcpy(response
, input
+ input_len
- hash_len
* 2, hash_len
* 2);
304 response
[hash_len
* 2 + 1] = '\0';
306 res
= cb_cram_md5 (sctx
, username
, challenge
, response
);
310 else if (cb_retrieve
)
320 md5h
= gcry_md_open (GCRY_MD_MD5
, GCRY_MD_FLAG_HMAC
);
324 return GSASL_GCRYPT_ERROR
;
327 res
= cb_retrieve(sctx
, username
, NULL
, NULL
, NULL
, &keylen
);
330 key
= malloc(keylen
);
332 return GSASL_MALLOC_ERROR
;
333 res
= cb_retrieve(sctx
, username
, NULL
, NULL
, key
, &keylen
);
340 normkey
= gsasl_utf8_nfkc_normalize (key
, keylen
);
345 return GSASL_UNICODE_NORMALIZATION_ERROR
;
348 res
= gcry_md_setkey (md5h
, normkey
, strlen(normkey
));
350 if (res
!= GCRYERR_SUCCESS
)
353 return GSASL_GCRYPT_ERROR
;
356 gcry_md_write (md5h
, challenge
, strlen(challenge
));
358 hash
= gcry_md_read (md5h
, GCRY_MD_MD5
);
362 return GSASL_GCRYPT_ERROR
;
366 for (i
= 0; i
< hash_len
; i
++)
367 if ((input
[input_len
- hash_len
*2 + 2*i
+ 1] != HEXCHAR(hash
[i
])) ||
368 (input
[input_len
- hash_len
*2 + 2*i
+ 0] != HEXCHAR(hash
[i
] >> 4)))
369 res
= GSASL_AUTHENTICATION_ERROR
;
378 _gsasl_cram_md5_server_finish (Gsasl_session_ctx
*sctx
,
381 char *challenge
= mech_data
;
388 #endif /* USE_CRAM_MD5 */