13584Ssos/*- 23584Ssos * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il> 3230132Suqs * All rights reserved. 411397Sswallace * 53584Ssos * Redistribution and use in source and binary forms, with or without 63584Ssos * modification, are permitted provided that the following conditions 73584Ssos * are met: 83584Ssos * 1. Redistributions of source code must retain the above copyright 93584Ssos * notice, this list of conditions and the following disclaimer. 103584Ssos * 2. Redistributions in binary form must reproduce the above copyright 113584Ssos * notice, this list of conditions and the following disclaimer in the 123584Ssos * documentation and/or other materials provided with the distribution. 133584Ssos * 143584Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 153584Ssos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 163584Ssos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1797748Sschweikh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 183584Ssos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 193584Ssos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 203584Ssos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 213584Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 223584Ssos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 233584Ssos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 243584Ssos * SUCH DAMAGE. 253584Ssos * 263584Ssos */ 273584Ssos/* 283584Ssos | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $ 293584Ssos */ 303584Ssos 31115684Sobrien#include <sys/cdefs.h> 32115684Sobrien__FBSDID("$FreeBSD$"); 33115684Sobrien 343584Ssos#include <sys/param.h> 3511397Sswallace#include <sys/types.h> 3614885Sswallace#include <sys/socket.h> 3711397Sswallace#include <sys/sysctl.h> 38162954Sphk 3991388Srobert#include <netinet/in.h> 4011397Sswallace#include <netinet/tcp.h> 41141488Sjhb#include <arpa/inet.h> 4224205Sbde#include <sys/ioctl.h> 4311397Sswallace#include <stdio.h> 44141488Sjhb#include <stdlib.h> 4511397Sswallace#include <string.h> 46160241Sjhb 47141488Sjhb#include <dev/iscsi_initiator/iscsi.h> 4811397Sswallace#include "iscontrol.h" 493584Ssos 503584Ssosstatic char *status_class1[] = { 5111397Sswallace "Initiator error", 5214885Sswallace "Authentication failure", 5311397Sswallace "Authorization failure", 5411397Sswallace "Not found", 5511397Sswallace "Target removed", 5611397Sswallace "Unsupported version", 5711397Sswallace "Too many connections", 583584Ssos "Missing parameter", 5954655Seivind "Can't include in session", 6011397Sswallace "Session type not suported", 6111397Sswallace "Session does not exist", 6211397Sswallace "Invalid during login", 6383366Sjulian}; 643584Ssos#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *)) 6583366Sjulian 6611397Sswallacestatic char *status_class3[] = { 67147820Sjhb "Target error", 68147820Sjhb "Service unavailable", 693584Ssos "Out of resources" 7011397Sswallace}; 7111397Sswallace#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *)) 7211397Sswallace 73160798Sjhbstatic char * 74147820SjhbselectFrom(char *str, token_t *list) 75160798Sjhb{ 76147820Sjhb char *sep, *sp; 77147820Sjhb token_t *lp; 783584Ssos int n; 793584Ssos 8011397Sswallace sp = str; 8183366Sjulian do { 8283366Sjulian sep = strchr(sp, ','); 8311397Sswallace if(sep != NULL) 8411397Sswallace n = sep - sp; 85160192Sjhb else 863584Ssos n = strlen(sp); 8711397Sswallace 88160192Sjhb for(lp = list; lp->name != NULL; lp++) { 89160192Sjhb if(strncasecmp(lp->name, sp, n) == 0) 90160192Sjhb return strdup(lp->name); 91160192Sjhb } 92160192Sjhb sp = sep + 1; 93160192Sjhb } while(sep != NULL); 9411397Sswallace 9511397Sswallace return NULL; 9611397Sswallace} 9783366Sjulian 9883366Sjulianstatic char * 9911397Sswallacegetkeyval(char *key, pdu_t *pp) 1003584Ssos{ 10111397Sswallace char *ptr; 1023584Ssos int klen, len, n; 10311397Sswallace 104107849Salfred debug_called(3); 105107849Salfred 106225617Skmacy len = pp->ds_len; 1073584Ssos ptr = (char *)pp->ds_addr; 1083584Ssos klen = strlen(key); 1093584Ssos while(len > klen) { 11011397Sswallace if(strncmp(key, ptr, klen) == 0) 11183366Sjulian return ptr+klen; 11283366Sjulian n = strlen(ptr) + 1; 11311397Sswallace len -= n; 1143584Ssos ptr += n; 11511397Sswallace } 11626819Ssef return 0; 117103870Salfred} 11826819Ssef 11926819Ssefstatic int 12026819SsefhandleTgtResp(isess_t *sess, pdu_t *pp) 12126819Ssef{ 12211397Sswallace isc_opt_t *op = sess->op; 12311397Sswallace char *np, *rp, *d1, *d2; 12411397Sswallace int res, l1, l2; 12511397Sswallace 12611397Sswallace res = -1; 127110299Sphk if(((np = getkeyval("CHAP_N=", pp)) == NULL) || 128110299Sphk ((rp = getkeyval("CHAP_R=", pp)) == NULL)) 12911397Sswallace goto out; 130107849Salfred if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) { 13126819Ssef fprintf(stderr, "%s does not match\n", np); 13211397Sswallace goto out; 13311397Sswallace } 13411397Sswallace l1 = str2bin(op->tgtChapDigest, &d1); 13583366Sjulian l2 = str2bin(rp, &d2); 13611397Sswallace 1373584Ssos debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp); 1383584Ssos if(l1 == l2 && memcmp(d1, d2, l1) == 0) 139107849Salfred res = 0; 140107849Salfred if(l1) 1413584Ssos free(d1); 142167086Sjhb if(l2) 1433584Ssos free(d2); 1443584Ssos out: 1453584Ssos free(op->tgtChapDigest); 14611397Sswallace op->tgtChapDigest = NULL; 14783366Sjulian 1483584Ssos debug(3, "res=%d", res); 1493584Ssos 1503584Ssos return res; 1513584Ssos} 1523584Ssos 1533584Ssosstatic void 1543584SsosprocessParams(isess_t *sess, pdu_t *pp) 1553584Ssos{ 1563584Ssos isc_opt_t *op = sess->op; 1573584Ssos int len, klen, n; 1583584Ssos char *eq, *ptr; 1593584Ssos 1603584Ssos debug_called(3); 1613584Ssos 1623584Ssos len = pp->ds_len; 16311397Sswallace ptr = (char *)pp->ds_addr; 1643584Ssos while(len > 0) { 16541514Sarchie if(vflag > 1) 16641514Sarchie printf("got: len=%d %s\n", len, ptr); 16791393Srobert klen = 0; 16891388Srobert if((eq = strchr(ptr, '=')) != NULL) 16941514Sarchie klen = eq - ptr; 17041514Sarchie if(klen > 0) { 17141514Sarchie if(strncmp(ptr, "TargetAddress", klen) == 0) { 17241514Sarchie char *p, *q, *ta = NULL; 17341514Sarchie 17441514Sarchie // TargetAddress=domainname[:port][,portal-group-tag] 17541514Sarchie // XXX: if(op->targetAddress) free(op->targetAddress); 17641514Sarchie q = op->targetAddress = strdup(eq+1); 17741514Sarchie if(*q == '[') { 17841514Sarchie // bracketed IPv6 17941514Sarchie if((q = strchr(q, ']')) != NULL) { 18041514Sarchie *q++ = '\0'; 1813584Ssos ta = op->targetAddress; 1823584Ssos op->targetAddress = strdup(ta+1); 1833584Ssos } else 18438354Sbde q = op->targetAddress; 18538354Sbde } 1863584Ssos if((p = strchr(q, ',')) != NULL) { 1873584Ssos *p++ = 0; 1883584Ssos op->targetPortalGroupTag = atoi(p); 1893584Ssos } 19083366Sjulian if((p = strchr(q, ':')) != NULL) { 1913584Ssos *p++ = 0; 19211397Sswallace op->port = atoi(p); 19383366Sjulian } 19411397Sswallace if(ta) 1953584Ssos free(ta); 19614885Sswallace } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) { 19714885Sswallace // danny's RFC 19883366Sjulian op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0); 19914885Sswallace } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) { 200141488Sjhb op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0); 201141488Sjhb } else if(strncmp(ptr, "HeaderDigest", klen) == 0) { 20214885Sswallace op->headerDigest = selectFrom(eq+1, DigestMethods); 203141488Sjhb } else if(strncmp(ptr, "DataDigest", klen) == 0) { 204141488Sjhb op->dataDigest = selectFrom(eq+1, DigestMethods); 205141488Sjhb } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0) 206141488Sjhb op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0); 207141488Sjhb#if 0 208141488Sjhb else 209141488Sjhb for(kp = keyMap; kp->name; kp++) { 21014885Sswallace if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=') 211141488Sjhb mp->func(sess, ptr+kp->len+1, GET); 212155401Sjhb } 213141488Sjhb#endif 214141488Sjhb } 21514885Sswallace n = strlen(ptr) + 1; 216 len -= n; 217 ptr += n; 218 } 219 220} 221 222static int 223handleLoginResp(isess_t *sess, pdu_t *pp) 224{ 225 login_rsp_t *lp = (login_rsp_t *)pp; 226 uint st_class, status = ntohs(lp->status); 227 228 debug_called(3); 229 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status); 230 231 st_class = status >> 8; 232 if(status) { 233 uint st_detail = status & 0xff; 234 235 switch(st_class) { 236 case 1: // Redirect 237 switch(st_detail) { 238 // the ITN (iSCSI target Name) requests a: 239 case 1: // temporary address change 240 case 2: // permanent address change 241 status = 0; 242 } 243 break; 244 245 case 2: // Initiator Error 246 if(st_detail < CLASS1_ERRS) 247 printf("0x%04x: %s\n", status, status_class1[st_detail]); 248 break; 249 250 case 3: 251 if(st_detail < CLASS3_ERRS) 252 printf("0x%04x: %s\n", status, status_class3[st_detail]); 253 break; 254 } 255 } 256 257 if(status == 0) { 258 processParams(sess, pp); 259 setOptions(sess, 0); // XXX: just in case ... 260 261 if(lp->T) { 262 isc_opt_t *op = sess->op; 263 264 if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL)) 265 if(handleTgtResp(sess, pp) != 0) 266 return 1; // XXX: Authentication failure ... 267 sess->csg = lp->NSG; 268 if(sess->csg == FF_PHASE) { 269 // XXX: will need this when implementing reconnect. 270 sess->tsih = lp->tsih; 271 debug(2, "TSIH=%x", sess->tsih); 272 } 273 } 274 } 275 276 return st_class; 277} 278 279static int 280handleChap(isess_t *sess, pdu_t *pp) 281{ 282 pdu_t spp; 283 login_req_t *lp; 284 isc_opt_t *op = sess->op; 285 char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits 286 287 debug_called(3); 288 289 bzero(&spp, sizeof(pdu_t)); 290 lp = (login_req_t *)&spp.ipdu.bhs; 291 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 292 memcpy(lp->isid, sess->isid, 6); 293 lp->tsih = sess->tsih; // MUST be zero the first time! 294 lp->CID = htons(1); 295 lp->CSG = SN_PHASE; // Security Negotiation 296 lp->NSG = LON_PHASE; 297 lp->T = 1; 298 299 if(((ap = getkeyval("CHAP_A=", pp)) == NULL) || 300 ((ip = getkeyval("CHAP_I=", pp)) == NULL) || 301 ((cp = getkeyval("CHAP_C=", pp)) == NULL)) 302 return -1; 303 304 if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL) 305 return -1; 306 307 addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName); 308 addText(&spp, "CHAP_R=%s", digest); 309 free(digest); 310 311 if(op->tgtChapSecret != NULL) { 312 op->tgtChapID = (random() >> 24) % 255; // should be random enough ... 313 addText(&spp, "CHAP_I=%d", op->tgtChapID); 314 cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8); 315 addText(&spp, "CHAP_C=%s", cp); 316 op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret); 317 } 318 319 return sendPDU(sess, &spp, handleLoginResp); 320} 321 322static int 323authenticate(isess_t *sess) 324{ 325 pdu_t spp; 326 login_req_t *lp; 327 isc_opt_t *op = sess->op; 328 329 bzero(&spp, sizeof(pdu_t)); 330 lp = (login_req_t *)&spp.ipdu.bhs; 331 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 332 memcpy(lp->isid, sess->isid, 6); 333 lp->tsih = sess->tsih; // MUST be zero the first time! 334 lp->CID = htons(1); 335 lp->CSG = SN_PHASE; // Security Negotiation 336 lp->NSG = SN_PHASE; 337 lp->T = 0; 338 339 switch((authm_t)lookup(AuthMethods, op->authMethod)) { 340 case NONE: 341 return 0; 342 343 case KRB5: 344 case SPKM1: 345 case SPKM2: 346 case SRP: 347 return 2; 348 349 case CHAP: 350 if(op->chapDigest == 0) 351 addText(&spp, "CHAP_A=5"); 352 else 353 if(strcmp(op->chapDigest, "MD5") == 0) 354 addText(&spp, "CHAP_A=5"); 355 else 356 if(strcmp(op->chapDigest, "SHA1") == 0) 357 addText(&spp, "CHAP_A=7"); 358 else 359 addText(&spp, "CHAP_A=5,7"); 360 return sendPDU(sess, &spp, handleChap); 361 } 362 return 1; 363} 364 365int 366loginPhase(isess_t *sess) 367{ 368 pdu_t spp, *sp = &spp; 369 isc_opt_t *op = sess->op; 370 login_req_t *lp; 371 int status = 1; 372 373 debug_called(3); 374 375 bzero(sp, sizeof(pdu_t)); 376 lp = (login_req_t *)&spp.ipdu.bhs; 377 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 378 memcpy(lp->isid, sess->isid, 6); 379 lp->tsih = sess->tsih; // MUST be zero the first time! 380 lp->CID = htons(1); // sess->cid? 381 382 if((lp->CSG = sess->csg) == LON_PHASE) 383 lp->NSG = FF_PHASE; // lets try and go full feature ... 384 else 385 lp->NSG = LON_PHASE; 386 lp->T = 1; // transit to next login stage 387 388 if(sess->flags & SESS_INITIALLOGIN1) { 389 sess->flags &= ~SESS_INITIALLOGIN1; 390 391 addText(sp, "SessionType=%s", op->sessionType); 392 addText(sp, "InitiatorName=%s", op->initiatorName); 393 if(strcmp(op->sessionType, "Discovery") != 0) { 394 addText(sp, "TargetName=%s", op->targetName); 395 } 396 } 397 switch(sess->csg) { 398 case SN_PHASE: // Security Negotiation 399 addText(sp, "AuthMethod=%s", op->authMethod); 400 break; 401 402 case LON_PHASE: // Login Operational Negotiation 403 if((sess->flags & SESS_NEGODONE) == 0) { 404 sess->flags |= SESS_NEGODONE; 405 addText(sp, "MaxBurstLength=%d", op->maxBurstLength); 406 addText(sp, "HeaderDigest=%s", op->headerDigest); 407 addText(sp, "DataDigest=%s", op->dataDigest); 408 addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength); 409 addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel); 410 addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait); 411 addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain); 412 addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No"); 413 addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No"); 414 addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T); 415 416 if(strcmp(op->sessionType, "Discovery") != 0) { 417 addText(sp, "MaxConnections=%d", op->maxConnections); 418 addText(sp, "FirstBurstLength=%d", op->firstBurstLength); 419 addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No"); 420 addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No"); 421 } 422 } 423 424 break; 425 } 426 427 status = sendPDU(sess, &spp, handleLoginResp); 428 429 switch(status) { 430 case 0: // all is ok ... 431 if(sess->csg == SN_PHASE) 432 /* 433 | if we are still here, then we need 434 | to exchange some secrets ... 435 */ 436 status = authenticate(sess); 437 } 438 439 return status; 440} 441