1171568Sscottl/*- 2211095Sdes * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il> 3171568Sscottl * All rights reserved. 4171568Sscottl * 5171568Sscottl * Redistribution and use in source and binary forms, with or without 6171568Sscottl * modification, are permitted provided that the following conditions 7171568Sscottl * are met: 8171568Sscottl * 1. Redistributions of source code must retain the above copyright 9171568Sscottl * notice, this list of conditions and the following disclaimer. 10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright 11171568Sscottl * notice, this list of conditions and the following disclaimer in the 12171568Sscottl * documentation and/or other materials provided with the distribution. 13171568Sscottl * 14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17171568Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24171568Sscottl * SUCH DAMAGE. 25171568Sscottl * 26171568Sscottl */ 27171568Sscottl/* 28171568Sscottl | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $ 29171568Sscottl */ 30171568Sscottl 31171568Sscottl#include <sys/cdefs.h> 32171568Sscottl__FBSDID("$FreeBSD$"); 33171568Sscottl 34171568Sscottl#include <sys/param.h> 35171568Sscottl#include <sys/types.h> 36171568Sscottl#include <sys/socket.h> 37171568Sscottl#include <sys/sysctl.h> 38171568Sscottl 39171568Sscottl#include <netinet/in.h> 40171568Sscottl#include <netinet/tcp.h> 41171568Sscottl#include <arpa/inet.h> 42171568Sscottl#include <sys/ioctl.h> 43171568Sscottl#include <stdio.h> 44171568Sscottl#include <stdlib.h> 45171568Sscottl#include <string.h> 46171568Sscottl 47211095Sdes#include <dev/iscsi/initiator/iscsi.h> 48171568Sscottl#include "iscontrol.h" 49171568Sscottl 50171568Sscottlstatic char *status_class1[] = { 51171568Sscottl "Initiator error", 52171568Sscottl "Authentication failure", 53171568Sscottl "Authorization failure", 54171568Sscottl "Not found", 55171568Sscottl "Target removed", 56171568Sscottl "Unsupported version", 57171568Sscottl "Too many connections", 58171568Sscottl "Missing parameter", 59171568Sscottl "Can't include in session", 60171568Sscottl "Session type not suported", 61171568Sscottl "Session does not exist", 62171568Sscottl "Invalid during login", 63171568Sscottl}; 64171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *)) 65171568Sscottl 66171568Sscottlstatic char *status_class3[] = { 67171568Sscottl "Target error", 68171568Sscottl "Service unavailable", 69171568Sscottl "Out of resources" 70171568Sscottl}; 71171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *)) 72171568Sscottl 73171568Sscottlstatic char * 74171568SscottlselectFrom(char *str, token_t *list) 75171568Sscottl{ 76171568Sscottl char *sep, *sp; 77171568Sscottl token_t *lp; 78171568Sscottl int n; 79171568Sscottl 80171568Sscottl sp = str; 81171568Sscottl do { 82171568Sscottl sep = strchr(sp, ','); 83171568Sscottl if(sep != NULL) 84171568Sscottl n = sep - sp; 85171568Sscottl else 86171568Sscottl n = strlen(sp); 87171568Sscottl 88171568Sscottl for(lp = list; lp->name != NULL; lp++) { 89171568Sscottl if(strncasecmp(lp->name, sp, n) == 0) 90171568Sscottl return strdup(lp->name); 91171568Sscottl } 92171568Sscottl sp = sep + 1; 93171568Sscottl } while(sep != NULL); 94171568Sscottl 95171568Sscottl return NULL; 96171568Sscottl} 97171568Sscottl 98171568Sscottlstatic char * 99171568Sscottlgetkeyval(char *key, pdu_t *pp) 100171568Sscottl{ 101171568Sscottl char *ptr; 102171568Sscottl int klen, len, n; 103171568Sscottl 104171568Sscottl debug_called(3); 105171568Sscottl 106171568Sscottl len = pp->ds_len; 107211095Sdes ptr = (char *)pp->ds_addr; 108171568Sscottl klen = strlen(key); 109171568Sscottl while(len > klen) { 110171568Sscottl if(strncmp(key, ptr, klen) == 0) 111171568Sscottl return ptr+klen; 112171568Sscottl n = strlen(ptr) + 1; 113171568Sscottl len -= n; 114171568Sscottl ptr += n; 115171568Sscottl } 116171568Sscottl return 0; 117171568Sscottl} 118171568Sscottl 119171568Sscottlstatic int 120171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp) 121171568Sscottl{ 122171568Sscottl isc_opt_t *op = sess->op; 123171568Sscottl char *np, *rp, *d1, *d2; 124171568Sscottl int res, l1, l2; 125171568Sscottl 126171568Sscottl res = -1; 127171568Sscottl if(((np = getkeyval("CHAP_N=", pp)) == NULL) || 128171568Sscottl ((rp = getkeyval("CHAP_R=", pp)) == NULL)) 129171568Sscottl goto out; 130171568Sscottl if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) { 131171568Sscottl fprintf(stderr, "%s does not match\n", np); 132171568Sscottl goto out; 133171568Sscottl } 134171568Sscottl l1 = str2bin(op->tgtChapDigest, &d1); 135171568Sscottl l2 = str2bin(rp, &d2); 136171568Sscottl 137171568Sscottl debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp); 138171568Sscottl if(l1 == l2 && memcmp(d1, d2, l1) == 0) 139171568Sscottl res = 0; 140171568Sscottl if(l1) 141171568Sscottl free(d1); 142171568Sscottl if(l2) 143171568Sscottl free(d2); 144171568Sscottl out: 145171568Sscottl free(op->tgtChapDigest); 146171568Sscottl op->tgtChapDigest = NULL; 147171568Sscottl 148171568Sscottl debug(3, "res=%d", res); 149171568Sscottl 150171568Sscottl return res; 151171568Sscottl} 152171568Sscottl 153171568Sscottlstatic void 154171568SscottlprocessParams(isess_t *sess, pdu_t *pp) 155171568Sscottl{ 156171568Sscottl isc_opt_t *op = sess->op; 157171568Sscottl int len, klen, n; 158171568Sscottl char *eq, *ptr; 159171568Sscottl 160171568Sscottl debug_called(3); 161171568Sscottl 162171568Sscottl len = pp->ds_len; 163211095Sdes ptr = (char *)pp->ds_addr; 164171568Sscottl while(len > 0) { 165171568Sscottl if(vflag > 1) 166171568Sscottl printf("got: len=%d %s\n", len, ptr); 167171568Sscottl klen = 0; 168171568Sscottl if((eq = strchr(ptr, '=')) != NULL) 169171568Sscottl klen = eq - ptr; 170171568Sscottl if(klen > 0) { 171171568Sscottl if(strncmp(ptr, "TargetAddress", klen) == 0) { 172185289Sscottl char *p, *q, *ta = NULL; 173171568Sscottl 174171568Sscottl // TargetAddress=domainname[:port][,portal-group-tag] 175171568Sscottl // XXX: if(op->targetAddress) free(op->targetAddress); 176171568Sscottl q = op->targetAddress = strdup(eq+1); 177171568Sscottl if(*q == '[') { 178171568Sscottl // bracketed IPv6 179185289Sscottl if((q = strchr(q, ']')) != NULL) { 180185289Sscottl *q++ = '\0'; 181185289Sscottl ta = op->targetAddress; 182185289Sscottl op->targetAddress = strdup(ta+1); 183185289Sscottl } else 184171568Sscottl q = op->targetAddress; 185171568Sscottl } 186171568Sscottl if((p = strchr(q, ',')) != NULL) { 187171568Sscottl *p++ = 0; 188171568Sscottl op->targetPortalGroupTag = atoi(p); 189171568Sscottl } 190171568Sscottl if((p = strchr(q, ':')) != NULL) { 191171568Sscottl *p++ = 0; 192171568Sscottl op->port = atoi(p); 193171568Sscottl } 194185289Sscottl if(ta) 195185289Sscottl free(ta); 196171568Sscottl } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) { 197171568Sscottl // danny's RFC 198171568Sscottl op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0); 199171568Sscottl } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) { 200171568Sscottl op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0); 201171568Sscottl } else if(strncmp(ptr, "HeaderDigest", klen) == 0) { 202171568Sscottl op->headerDigest = selectFrom(eq+1, DigestMethods); 203171568Sscottl } else if(strncmp(ptr, "DataDigest", klen) == 0) { 204171568Sscottl op->dataDigest = selectFrom(eq+1, DigestMethods); 205171568Sscottl } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0) 206171568Sscottl op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0); 207171568Sscottl#if 0 208171568Sscottl else 209171568Sscottl for(kp = keyMap; kp->name; kp++) { 210171568Sscottl if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=') 211171568Sscottl mp->func(sess, ptr+kp->len+1, GET); 212171568Sscottl } 213171568Sscottl#endif 214171568Sscottl } 215171568Sscottl n = strlen(ptr) + 1; 216171568Sscottl len -= n; 217171568Sscottl ptr += n; 218171568Sscottl } 219171568Sscottl 220171568Sscottl} 221171568Sscottl 222171568Sscottlstatic int 223171568SscottlhandleLoginResp(isess_t *sess, pdu_t *pp) 224171568Sscottl{ 225171568Sscottl login_rsp_t *lp = (login_rsp_t *)pp; 226171568Sscottl uint st_class, status = ntohs(lp->status); 227171568Sscottl 228171568Sscottl debug_called(3); 229171568Sscottl debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status); 230171568Sscottl 231171568Sscottl st_class = status >> 8; 232171568Sscottl if(status) { 233211095Sdes uint st_detail = status & 0xff; 234171568Sscottl 235171568Sscottl switch(st_class) { 236171568Sscottl case 1: // Redirect 237171568Sscottl switch(st_detail) { 238171568Sscottl // the ITN (iSCSI target Name) requests a: 239171568Sscottl case 1: // temporary address change 240171568Sscottl case 2: // permanent address change 241171568Sscottl status = 0; 242171568Sscottl } 243171568Sscottl break; 244171568Sscottl 245171568Sscottl case 2: // Initiator Error 246171568Sscottl if(st_detail < CLASS1_ERRS) 247171568Sscottl printf("0x%04x: %s\n", status, status_class1[st_detail]); 248171568Sscottl break; 249171568Sscottl 250171568Sscottl case 3: 251171568Sscottl if(st_detail < CLASS3_ERRS) 252171568Sscottl printf("0x%04x: %s\n", status, status_class3[st_detail]); 253171568Sscottl break; 254171568Sscottl } 255171568Sscottl } 256171568Sscottl 257171568Sscottl if(status == 0) { 258171568Sscottl processParams(sess, pp); 259171568Sscottl setOptions(sess, 0); // XXX: just in case ... 260171568Sscottl 261171568Sscottl if(lp->T) { 262171568Sscottl isc_opt_t *op = sess->op; 263171568Sscottl 264171568Sscottl if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL)) 265171568Sscottl if(handleTgtResp(sess, pp) != 0) 266171568Sscottl return 1; // XXX: Authentication failure ... 267171568Sscottl sess->csg = lp->NSG; 268171568Sscottl if(sess->csg == FF_PHASE) { 269171568Sscottl // XXX: will need this when implementing reconnect. 270171568Sscottl sess->tsih = lp->tsih; 271171568Sscottl debug(2, "TSIH=%x", sess->tsih); 272171568Sscottl } 273171568Sscottl } 274171568Sscottl } 275171568Sscottl 276171568Sscottl return st_class; 277171568Sscottl} 278171568Sscottl 279171568Sscottlstatic int 280171568SscottlhandleChap(isess_t *sess, pdu_t *pp) 281171568Sscottl{ 282171568Sscottl pdu_t spp; 283171568Sscottl login_req_t *lp; 284171568Sscottl isc_opt_t *op = sess->op; 285171568Sscottl char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits 286171568Sscottl 287171568Sscottl debug_called(3); 288171568Sscottl 289171568Sscottl bzero(&spp, sizeof(pdu_t)); 290171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 291171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 292171568Sscottl memcpy(lp->isid, sess->isid, 6); 293171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 294171568Sscottl lp->CID = htons(1); 295171568Sscottl lp->CSG = SN_PHASE; // Security Negotiation 296171568Sscottl lp->NSG = LON_PHASE; 297171568Sscottl lp->T = 1; 298171568Sscottl 299171568Sscottl if(((ap = getkeyval("CHAP_A=", pp)) == NULL) || 300171568Sscottl ((ip = getkeyval("CHAP_I=", pp)) == NULL) || 301171568Sscottl ((cp = getkeyval("CHAP_C=", pp)) == NULL)) 302171568Sscottl return -1; 303171568Sscottl 304171568Sscottl if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL) 305171568Sscottl return -1; 306171568Sscottl 307171568Sscottl addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName); 308171568Sscottl addText(&spp, "CHAP_R=%s", digest); 309171568Sscottl free(digest); 310171568Sscottl 311171568Sscottl if(op->tgtChapSecret != NULL) { 312171568Sscottl op->tgtChapID = (random() >> 24) % 255; // should be random enough ... 313171568Sscottl addText(&spp, "CHAP_I=%d", op->tgtChapID); 314171568Sscottl cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8); 315171568Sscottl addText(&spp, "CHAP_C=%s", cp); 316171568Sscottl op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret); 317171568Sscottl } 318171568Sscottl 319171568Sscottl return sendPDU(sess, &spp, handleLoginResp); 320171568Sscottl} 321171568Sscottl 322171568Sscottlstatic int 323171568Sscottlauthenticate(isess_t *sess) 324171568Sscottl{ 325171568Sscottl pdu_t spp; 326171568Sscottl login_req_t *lp; 327171568Sscottl isc_opt_t *op = sess->op; 328171568Sscottl 329171568Sscottl bzero(&spp, sizeof(pdu_t)); 330171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 331171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 332171568Sscottl memcpy(lp->isid, sess->isid, 6); 333171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 334171568Sscottl lp->CID = htons(1); 335171568Sscottl lp->CSG = SN_PHASE; // Security Negotiation 336171568Sscottl lp->NSG = SN_PHASE; 337171568Sscottl lp->T = 0; 338171568Sscottl 339171568Sscottl switch((authm_t)lookup(AuthMethods, op->authMethod)) { 340171568Sscottl case NONE: 341171568Sscottl return 0; 342171568Sscottl 343171568Sscottl case KRB5: 344171568Sscottl case SPKM1: 345171568Sscottl case SPKM2: 346171568Sscottl case SRP: 347171568Sscottl return 2; 348171568Sscottl 349171568Sscottl case CHAP: 350171568Sscottl if(op->chapDigest == 0) 351171568Sscottl addText(&spp, "CHAP_A=5"); 352171568Sscottl else 353171568Sscottl if(strcmp(op->chapDigest, "MD5") == 0) 354171568Sscottl addText(&spp, "CHAP_A=5"); 355171568Sscottl else 356171568Sscottl if(strcmp(op->chapDigest, "SHA1") == 0) 357171568Sscottl addText(&spp, "CHAP_A=7"); 358171568Sscottl else 359171568Sscottl addText(&spp, "CHAP_A=5,7"); 360171568Sscottl return sendPDU(sess, &spp, handleChap); 361171568Sscottl } 362171568Sscottl return 1; 363171568Sscottl} 364171568Sscottl 365171568Sscottlint 366171568SscottlloginPhase(isess_t *sess) 367171568Sscottl{ 368171568Sscottl pdu_t spp, *sp = &spp; 369171568Sscottl isc_opt_t *op = sess->op; 370171568Sscottl login_req_t *lp; 371171568Sscottl int status = 1; 372171568Sscottl 373171568Sscottl debug_called(3); 374171568Sscottl 375171568Sscottl bzero(sp, sizeof(pdu_t)); 376171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 377171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 378171568Sscottl memcpy(lp->isid, sess->isid, 6); 379171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 380171568Sscottl lp->CID = htons(1); // sess->cid? 381171568Sscottl 382171568Sscottl if((lp->CSG = sess->csg) == LON_PHASE) 383171568Sscottl lp->NSG = FF_PHASE; // lets try and go full feature ... 384171568Sscottl else 385171568Sscottl lp->NSG = LON_PHASE; 386171568Sscottl lp->T = 1; // transit to next login stage 387171568Sscottl 388171568Sscottl if(sess->flags & SESS_INITIALLOGIN1) { 389171568Sscottl sess->flags &= ~SESS_INITIALLOGIN1; 390171568Sscottl 391171568Sscottl addText(sp, "SessionType=%s", op->sessionType); 392171568Sscottl addText(sp, "InitiatorName=%s", op->initiatorName); 393171568Sscottl if(strcmp(op->sessionType, "Discovery") != 0) { 394171568Sscottl addText(sp, "TargetName=%s", op->targetName); 395171568Sscottl } 396171568Sscottl } 397171568Sscottl switch(sess->csg) { 398171568Sscottl case SN_PHASE: // Security Negotiation 399171568Sscottl addText(sp, "AuthMethod=%s", op->authMethod); 400171568Sscottl break; 401171568Sscottl 402171568Sscottl case LON_PHASE: // Login Operational Negotiation 403171568Sscottl if((sess->flags & SESS_NEGODONE) == 0) { 404171568Sscottl sess->flags |= SESS_NEGODONE; 405171568Sscottl addText(sp, "MaxBurstLength=%d", op->maxBurstLength); 406171568Sscottl addText(sp, "HeaderDigest=%s", op->headerDigest); 407171568Sscottl addText(sp, "DataDigest=%s", op->dataDigest); 408171568Sscottl addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength); 409171568Sscottl addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel); 410171568Sscottl addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait); 411171568Sscottl addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain); 412171568Sscottl addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No"); 413171568Sscottl addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No"); 414171568Sscottl addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T); 415171568Sscottl 416171568Sscottl if(strcmp(op->sessionType, "Discovery") != 0) { 417171568Sscottl addText(sp, "MaxConnections=%d", op->maxConnections); 418171568Sscottl addText(sp, "FirstBurstLength=%d", op->firstBurstLength); 419171568Sscottl addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No"); 420171568Sscottl addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No"); 421171568Sscottl } 422171568Sscottl } 423171568Sscottl 424171568Sscottl break; 425171568Sscottl } 426171568Sscottl 427171568Sscottl status = sendPDU(sess, &spp, handleLoginResp); 428171568Sscottl 429171568Sscottl switch(status) { 430171568Sscottl case 0: // all is ok ... 431171568Sscottl if(sess->csg == SN_PHASE) 432171568Sscottl /* 433171568Sscottl | if we are still here, then we need 434171568Sscottl | to exchange some secrets ... 435171568Sscottl */ 436171568Sscottl status = authenticate(sess); 437171568Sscottl } 438171568Sscottl 439171568Sscottl return status; 440171568Sscottl} 441