1/* 2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <unistd.h> 39#include <roken.h> 40#include <err.h> 41#include "heim-auth.h" 42 43static int 44test_sasl_digest_md5(void) 45{ 46 heim_digest_t ctx; 47 const char *user, *challenge, *resp; 48 char *r; 49 50 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL) 51 abort(); 52 53 if (heim_digest_parse_challenge(ctx, "realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",qop=\"auth\",algorithm=md5-sess,charset=utf-8")) 54 abort(); 55 56 /* check that server detects changing QOP */ 57 if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-int")) 58 errx(1, "don't detect changing qop"); 59 60 /* should pass */ 61 if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth")) 62 abort(); 63 64 if ((user = heim_digest_get_key(ctx, "username")) == NULL) 65 abort(); 66 if (strcmp(user, "chris") != 0) 67 abort(); 68 69 /* 70 * check password 71 */ 72 73 heim_digest_set_key(ctx, "password", "secret"); 74 75 if (heim_digest_verify(ctx, &r)) 76 abort(); 77 78 if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0) 79 abort(); 80 81 free(r); 82 83 /* 84 * Also check userhash 85 */ 86 87 r = heim_digest_userhash("chris", "elwood.innosoft.com", "secret"); 88 if (strcmp(r, "eb5a750053e4d2c34aa84bbc9b0b6ee7") != 0) 89 abort(); 90 91 heim_digest_set_key(ctx, "userhash", r); 92 free(r); 93 94 if (heim_digest_verify(ctx, &r)) 95 abort(); 96 97 if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0) 98 abort(); 99 100 free(r); 101 102 /* check that it failes */ 103 104 heim_digest_set_key(ctx, "username", "notright"); 105 heim_digest_set_key(ctx, "password", "secret"); 106 107 if (heim_digest_verify(ctx, &r) == 0) 108 abort(); 109 110 if ((user = heim_digest_get_key(ctx, "username")) == NULL) 111 abort(); 112 if (strcmp(user, "notright") != 0) 113 abort(); 114 115 116 /* Done */ 117 118 heim_digest_release(ctx); 119 120 121 /* 122 * Check heim_digest_generate_challenge() 123 */ 124 125 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL) 126 abort(); 127 128 heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com"); 129 heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh"); 130 heim_digest_set_key(ctx, "serverQOP", "auth,auth-int"); 131 132 challenge = heim_digest_generate_challenge(ctx); 133 if (challenge == NULL) 134 abort(); 135 136 if (heim_digest_parse_challenge(ctx, challenge)) 137 abort(); 138 139 /* check that server detects changing QOP */ 140 if (!heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth-conf")) 141 abort(); 142 143 if (heim_digest_parse_response(ctx, "charset=utf-8,username=\"chris\",realm=\"elwood.innosoft.com\",nonce=\"OA6MG9tEQGm2hh\",nc=00000001,cnonce=\"OA6MHXh6VqTrRk\",digest-uri=\"imap/elwood.innosoft.com\",response=d388dad90d4bbd760a152321f2143af7,qop=auth")) 144 abort(); 145 146 heim_digest_set_key(ctx, "password", "secret"); 147 148 if (heim_digest_verify(ctx, &r)) 149 abort(); 150 151 if (strcmp(r, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0) 152 abort(); 153 154 free(r); 155 156 heim_digest_release(ctx); 157 158 /* 159 * Validate heim_digest_service_response() 160 */ 161 162 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL) 163 abort(); 164 165 heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk"); 166 heim_digest_set_key(ctx, "clientQOP", "auth"); 167 heim_digest_set_key(ctx, "clientNC", "00000001"); 168 heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com"); 169 heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com"); 170 heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh"); 171 heim_digest_set_key(ctx, "userhash", "eb5a750053e4d2c34aa84bbc9b0b6ee7"); 172 173 resp = heim_digest_server_response(ctx); 174 175 if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0) 176 abort(); 177 178 heim_digest_release(ctx); 179 180 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL) 181 abort(); 182 183 heim_digest_set_key(ctx, "clientNonce", "OA6MHXh6VqTrRk"); 184 heim_digest_set_key(ctx, "clientQOP", "auth"); 185 heim_digest_set_key(ctx, "clientNC", "00000001"); 186 heim_digest_set_key(ctx, "clientURI", "imap/elwood.innosoft.com"); 187 heim_digest_set_key(ctx, "serverRealm", "elwood.innosoft.com"); 188 heim_digest_set_key(ctx, "serverNonce", "OA6MG9tEQGm2hh"); 189 heim_digest_set_key(ctx, "password", "secret"); 190 heim_digest_set_key(ctx, "username", "chris"); 191 192 resp = heim_digest_server_response(ctx); 193 194 if (resp == NULL || strcmp(resp, "rspauth=ea40f60335c427b5527b84dbabcdfffd") != 0) 195 abort(); 196 197 heim_digest_release(ctx); 198 199 return 0; 200} 201 202static int 203test_http_digest_md5(void) 204{ 205 heim_digest_t ctx, ctx2; 206 const char *user, *chal, *resp; 207 char *serverresp, *serverresp2; 208 209 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL) 210 abort(); 211 212 if (heim_digest_parse_challenge(ctx, "realm=\"testrealm@host.com\"," 213 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 214 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")) 215 abort(); 216 217 if (heim_digest_parse_response(ctx, "username=\"Mufasa\"," 218 "realm=\"testrealm@host.com\"," 219 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 220 "uri=\"/dir/index.html\"," 221 "response=\"1949323746fe6a43ef61f9606e7febea\"," 222 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")) 223 abort(); 224 225 if ((user = heim_digest_get_key(ctx, "username")) == NULL) 226 abort(); 227 if (strcmp(user, "Mufasa") != 0) 228 abort(); 229 230 if ((user = heim_digest_get_key(ctx, "clientUsername")) == NULL) 231 abort(); 232 if (strcmp(user, "Mufasa") != 0) 233 abort(); 234 235 heim_digest_set_key(ctx, "password", "CircleOfLife"); 236 237 if (heim_digest_verify(ctx, NULL)) 238 abort(); 239 240 /* Verify failure */ 241 242 heim_digest_set_key(ctx, "username", "Oskar"); 243 244 if (heim_digest_verify(ctx, NULL) == 0) 245 abort(); 246 247 heim_digest_release(ctx); 248 249 /* 250 * Check myself 251 */ 252 253 /* server */ 254 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL) 255 abort(); 256 257 heim_digest_set_key(ctx, "serverRealm", "myrealmhahaha"); 258 heim_digest_set_key(ctx, "serverQOP", "auth,auth-int"); 259 260 chal = heim_digest_generate_challenge(ctx); 261 if (chal == NULL) 262 abort(); 263 264 /* client */ 265 if ((ctx2 = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2831)) == NULL) 266 abort(); 267 268 if (heim_digest_parse_challenge(ctx2, chal)) 269 abort(); 270 271 heim_digest_set_key(ctx2, "username", "lha"); 272 heim_digest_set_key(ctx2, "password", "passw0rd"); 273 heim_digest_set_key(ctx2, "uri", "/uri"); 274 275 resp = heim_digest_create_response(ctx2, &serverresp); 276 if (resp == NULL) 277 abort(); 278 279 /* server */ 280 if (heim_digest_parse_response(ctx, resp)) 281 abort(); 282 283 heim_digest_set_key(ctx, "password", "passw0rd"); 284 heim_digest_verify(ctx, &serverresp2); 285 286 287 /* client */ 288 if (strcmp(serverresp, serverresp2) != 0) 289 abort(); 290 291 heim_digest_release(ctx); 292 heim_digest_release(ctx2); 293 294 /* 295 * check prefix 296 */ 297 298 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL) 299 abort(); 300 301 if (heim_digest_parse_challenge(ctx, "Digest realm=\"testrealm@host.com\"," 302 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 303 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")) 304 abort(); 305 306 heim_digest_release(ctx); 307 308 /* 309 * check prefix 310 */ 311 312 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_AUTO)) == NULL) 313 abort(); 314 315 if (heim_digest_parse_challenge(ctx, "Digest realm=\"testrealm@host.com\"," 316 "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"," 317 "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")) 318 abort(); 319 320 heim_digest_release(ctx); 321 322 /* 323 * Check md5-sess 324 */ 325 326 /* server */ 327 if ((ctx = heim_digest_create(1, HEIM_DIGEST_TYPE_MD5_SESS)) == NULL) 328 abort(); 329 330 heim_digest_set_key(ctx, "serverRealm", "myrealmhahaha"); 331 332 chal = heim_digest_generate_challenge(ctx); 333 if (chal == NULL) 334 abort(); 335 336 /* client */ 337 if ((ctx2 = heim_digest_create(1, HEIM_DIGEST_TYPE_RFC2617_MD5_SESS)) == NULL) 338 abort(); 339 340 if (heim_digest_parse_challenge(ctx2, chal)) 341 abort(); 342 343 heim_digest_set_key(ctx2, "username", "lha"); 344 heim_digest_set_key(ctx2, "password", "passw0rd"); 345 heim_digest_set_key(ctx2, "uri", "/uri"); 346 347 resp = heim_digest_create_response(ctx2, &serverresp); 348 if (resp == NULL) 349 abort(); 350 351 /* server */ 352 if (heim_digest_parse_response(ctx, resp)) 353 abort(); 354 355 heim_digest_set_key(ctx, "password", "passw0rd"); 356 heim_digest_verify(ctx, &serverresp2); 357 358 /* client */ 359 if (strcmp(serverresp, serverresp2) != 0) 360 abort(); 361 362 heim_digest_release(ctx); 363 heim_digest_release(ctx2); 364 365 366 return 0; 367} 368 369static int 370test_cram_md5(void) 371{ 372 const char *chal = "<1896.697170952@postoffice.reston.mci.net>"; 373 const char *secret = "tanstaaftanstaaf"; 374 const char *resp = "b913a602c7eda7a495b4e6e7334d3890"; 375 heim_CRAM_MD5_STATE state; 376 heim_cram_md5 ctx; 377 char *t; 378 379 const uint8_t *prestate = (uint8_t *) 380 "\x87\x1E\x24\x10\xB4\x0C\x72\x5D\xA3\x95\x2D\x5B\x8B\xFC\xDD\xE1" 381 "\x29\x90\xCB\xA7\x66\xF6\xB3\x40\xE8\xAC\x48\x2C\xE4\xE3\xA4\x40"; 382 383 /* 384 * Test prebuild blobs 385 */ 386 387 assert(sizeof(state) == 32); 388 389 heim_cram_md5_export("foo", &state); 390 391 if (memcmp(prestate, &state, 32) != 0) 392 abort(); 393 394 /* 395 * Check example 396 */ 397 398 399 if (heim_cram_md5_verify(chal, secret, resp) != 0) 400 abort(); 401 402 403 /* 404 * Do it ourself 405 */ 406 407 t = heim_cram_md5_create(chal, secret); 408 if (t == NULL) 409 abort(); 410 411 if (strcmp(resp, t) != 0) 412 abort(); 413 414 heim_cram_md5_export(secret, &state); 415 /* here you can store the memcpy-ed version of state somewhere else */ 416 417 ctx = heim_cram_md5_import(&state, sizeof(state)); 418 419 memset(&state, 0, sizeof(state)); 420 421 if (heim_cram_md5_verify_ctx(ctx, chal, resp) != 0) 422 abort(); 423 424 heim_cram_md5_free(ctx); 425 426 free(t); 427 428 return 0; 429} 430 431static int 432test_apop(void) 433{ 434 const char *chal = "<1896.697170952@dbc.mtview.ca.us>"; 435 const char *secret = "tanstaaf"; 436 const char *resp = "c4c9334bac560ecc979e58001b3e22fb"; 437 char *t; 438 439 t = heim_apop_create(chal, secret); 440 if (t == NULL) 441 abort(); 442 443 if (strcmp(resp, t) != 0) 444 abort(); 445 446 if (heim_apop_verify(chal, secret, resp) != 0) 447 abort(); 448 449 free(t); 450 451 return 0; 452} 453 454 455int 456main(int argc, char **argv) 457{ 458 int ret = 0; 459 460 ret |= test_sasl_digest_md5(); 461 ret |= test_http_digest_md5(); 462 ret |= test_cram_md5(); 463 ret |= test_apop(); 464 465 return ret; 466} 467