login.c revision 171568
1171568Sscottl/*- 2171568Sscottl * Copyright (c) 2005 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: head/sbin/iscontrol/login.c 171568 2007-07-24 15:35:02Z scottl $"); 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#if __FreeBSD_version < 500000 43171568Sscottl#include <sys/time.h> 44171568Sscottl#endif 45171568Sscottl#include <sys/ioctl.h> 46171568Sscottl#include <stdio.h> 47171568Sscottl#include <stdlib.h> 48171568Sscottl#include <string.h> 49171568Sscottl 50171568Sscottl#include "iscsi.h" 51171568Sscottl#include "iscontrol.h" 52171568Sscottl#include "pdu.h" 53171568Sscottl 54171568Sscottlstatic char *status_class1[] = { 55171568Sscottl "Initiator error", 56171568Sscottl "Authentication failure", 57171568Sscottl "Authorization failure", 58171568Sscottl "Not found", 59171568Sscottl "Target removed", 60171568Sscottl "Unsupported version", 61171568Sscottl "Too many connections", 62171568Sscottl "Missing parameter", 63171568Sscottl "Can't include in session", 64171568Sscottl "Session type not suported", 65171568Sscottl "Session does not exist", 66171568Sscottl "Invalid during login", 67171568Sscottl}; 68171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *)) 69171568Sscottl 70171568Sscottlstatic char *status_class3[] = { 71171568Sscottl "Target error", 72171568Sscottl "Service unavailable", 73171568Sscottl "Out of resources" 74171568Sscottl}; 75171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *)) 76171568Sscottl 77171568Sscottlstatic char * 78171568SscottlselectFrom(char *str, token_t *list) 79171568Sscottl{ 80171568Sscottl char *sep, *sp; 81171568Sscottl token_t *lp; 82171568Sscottl int n; 83171568Sscottl 84171568Sscottl sp = str; 85171568Sscottl do { 86171568Sscottl sep = strchr(sp, ','); 87171568Sscottl if(sep != NULL) 88171568Sscottl n = sep - sp; 89171568Sscottl else 90171568Sscottl n = strlen(sp); 91171568Sscottl 92171568Sscottl for(lp = list; lp->name != NULL; lp++) { 93171568Sscottl if(strncasecmp(lp->name, sp, n) == 0) 94171568Sscottl return strdup(lp->name); 95171568Sscottl } 96171568Sscottl sp = sep + 1; 97171568Sscottl } while(sep != NULL); 98171568Sscottl 99171568Sscottl return NULL; 100171568Sscottl} 101171568Sscottl 102171568Sscottlstatic char * 103171568Sscottlgetkeyval(char *key, pdu_t *pp) 104171568Sscottl{ 105171568Sscottl char *ptr; 106171568Sscottl int klen, len, n; 107171568Sscottl 108171568Sscottl debug_called(3); 109171568Sscottl 110171568Sscottl len = pp->ds_len; 111171568Sscottl ptr = (char *)pp->ds; 112171568Sscottl klen = strlen(key); 113171568Sscottl while(len > klen) { 114171568Sscottl if(strncmp(key, ptr, klen) == 0) 115171568Sscottl return ptr+klen; 116171568Sscottl n = strlen(ptr) + 1; 117171568Sscottl len -= n; 118171568Sscottl ptr += n; 119171568Sscottl } 120171568Sscottl return 0; 121171568Sscottl} 122171568Sscottl 123171568Sscottlstatic int 124171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp) 125171568Sscottl{ 126171568Sscottl isc_opt_t *op = sess->op; 127171568Sscottl char *np, *rp, *d1, *d2; 128171568Sscottl int res, l1, l2; 129171568Sscottl 130171568Sscottl res = -1; 131171568Sscottl if(((np = getkeyval("CHAP_N=", pp)) == NULL) || 132171568Sscottl ((rp = getkeyval("CHAP_R=", pp)) == NULL)) 133171568Sscottl goto out; 134171568Sscottl if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) { 135171568Sscottl fprintf(stderr, "%s does not match\n", np); 136171568Sscottl goto out; 137171568Sscottl } 138171568Sscottl l1 = str2bin(op->tgtChapDigest, &d1); 139171568Sscottl l2 = str2bin(rp, &d2); 140171568Sscottl 141171568Sscottl debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp); 142171568Sscottl if(l1 == l2 && memcmp(d1, d2, l1) == 0) 143171568Sscottl res = 0; 144171568Sscottl if(l1) 145171568Sscottl free(d1); 146171568Sscottl if(l2) 147171568Sscottl free(d2); 148171568Sscottl out: 149171568Sscottl free(op->tgtChapDigest); 150171568Sscottl op->tgtChapDigest = NULL; 151171568Sscottl 152171568Sscottl debug(3, "res=%d", res); 153171568Sscottl 154171568Sscottl return res; 155171568Sscottl} 156171568Sscottl 157171568Sscottlstatic void 158171568SscottlprocessParams(isess_t *sess, pdu_t *pp) 159171568Sscottl{ 160171568Sscottl isc_opt_t *op = sess->op; 161171568Sscottl int len, klen, n; 162171568Sscottl char *eq, *ptr; 163171568Sscottl 164171568Sscottl debug_called(3); 165171568Sscottl 166171568Sscottl len = pp->ds_len; 167171568Sscottl ptr = (char *)pp->ds; 168171568Sscottl while(len > 0) { 169171568Sscottl if(vflag > 1) 170171568Sscottl printf("got: len=%d %s\n", len, ptr); 171171568Sscottl klen = 0; 172171568Sscottl if((eq = strchr(ptr, '=')) != NULL) 173171568Sscottl klen = eq - ptr; 174171568Sscottl if(klen > 0) { 175171568Sscottl if(strncmp(ptr, "TargetAddress", klen) == 0) { 176171568Sscottl char *p, *q; 177171568Sscottl 178171568Sscottl // TargetAddress=domainname[:port][,portal-group-tag] 179171568Sscottl // XXX: if(op->targetAddress) free(op->targetAddress); 180171568Sscottl q = op->targetAddress = strdup(eq+1); 181171568Sscottl if(*q == '[') { 182171568Sscottl // bracketed IPv6 183171568Sscottl if((q = strchr(q, ']')) != NULL) 184171568Sscottl q++; 185171568Sscottl else 186171568Sscottl q = op->targetAddress; 187171568Sscottl } 188171568Sscottl if((p = strchr(q, ',')) != NULL) { 189171568Sscottl *p++ = 0; 190171568Sscottl op->targetPortalGroupTag = atoi(p); 191171568Sscottl } 192171568Sscottl if((p = strchr(q, ':')) != NULL) { 193171568Sscottl *p++ = 0; 194171568Sscottl op->port = atoi(p); 195171568Sscottl } 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) { 233171568Sscottl int 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