login.c revision 211095
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: head/sbin/iscontrol/login.c 211095 2010-08-09 12:36:36Z des $"); 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 50211095Sdes#include <dev/iscsi/initiator/iscsi.h> 51171568Sscottl#include "iscontrol.h" 52171568Sscottl 53171568Sscottlstatic char *status_class1[] = { 54171568Sscottl "Initiator error", 55171568Sscottl "Authentication failure", 56171568Sscottl "Authorization failure", 57171568Sscottl "Not found", 58171568Sscottl "Target removed", 59171568Sscottl "Unsupported version", 60171568Sscottl "Too many connections", 61171568Sscottl "Missing parameter", 62171568Sscottl "Can't include in session", 63171568Sscottl "Session type not suported", 64171568Sscottl "Session does not exist", 65171568Sscottl "Invalid during login", 66171568Sscottl}; 67171568Sscottl#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *)) 68171568Sscottl 69171568Sscottlstatic char *status_class3[] = { 70171568Sscottl "Target error", 71171568Sscottl "Service unavailable", 72171568Sscottl "Out of resources" 73171568Sscottl}; 74171568Sscottl#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *)) 75171568Sscottl 76171568Sscottlstatic char * 77171568SscottlselectFrom(char *str, token_t *list) 78171568Sscottl{ 79171568Sscottl char *sep, *sp; 80171568Sscottl token_t *lp; 81171568Sscottl int n; 82171568Sscottl 83171568Sscottl sp = str; 84171568Sscottl do { 85171568Sscottl sep = strchr(sp, ','); 86171568Sscottl if(sep != NULL) 87171568Sscottl n = sep - sp; 88171568Sscottl else 89171568Sscottl n = strlen(sp); 90171568Sscottl 91171568Sscottl for(lp = list; lp->name != NULL; lp++) { 92171568Sscottl if(strncasecmp(lp->name, sp, n) == 0) 93171568Sscottl return strdup(lp->name); 94171568Sscottl } 95171568Sscottl sp = sep + 1; 96171568Sscottl } while(sep != NULL); 97171568Sscottl 98171568Sscottl return NULL; 99171568Sscottl} 100171568Sscottl 101171568Sscottlstatic char * 102171568Sscottlgetkeyval(char *key, pdu_t *pp) 103171568Sscottl{ 104171568Sscottl char *ptr; 105171568Sscottl int klen, len, n; 106171568Sscottl 107171568Sscottl debug_called(3); 108171568Sscottl 109171568Sscottl len = pp->ds_len; 110211095Sdes ptr = (char *)pp->ds_addr; 111171568Sscottl klen = strlen(key); 112171568Sscottl while(len > klen) { 113171568Sscottl if(strncmp(key, ptr, klen) == 0) 114171568Sscottl return ptr+klen; 115171568Sscottl n = strlen(ptr) + 1; 116171568Sscottl len -= n; 117171568Sscottl ptr += n; 118171568Sscottl } 119171568Sscottl return 0; 120171568Sscottl} 121171568Sscottl 122171568Sscottlstatic int 123171568SscottlhandleTgtResp(isess_t *sess, pdu_t *pp) 124171568Sscottl{ 125171568Sscottl isc_opt_t *op = sess->op; 126171568Sscottl char *np, *rp, *d1, *d2; 127171568Sscottl int res, l1, l2; 128171568Sscottl 129171568Sscottl res = -1; 130171568Sscottl if(((np = getkeyval("CHAP_N=", pp)) == NULL) || 131171568Sscottl ((rp = getkeyval("CHAP_R=", pp)) == NULL)) 132171568Sscottl goto out; 133171568Sscottl if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) { 134171568Sscottl fprintf(stderr, "%s does not match\n", np); 135171568Sscottl goto out; 136171568Sscottl } 137171568Sscottl l1 = str2bin(op->tgtChapDigest, &d1); 138171568Sscottl l2 = str2bin(rp, &d2); 139171568Sscottl 140171568Sscottl debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp); 141171568Sscottl if(l1 == l2 && memcmp(d1, d2, l1) == 0) 142171568Sscottl res = 0; 143171568Sscottl if(l1) 144171568Sscottl free(d1); 145171568Sscottl if(l2) 146171568Sscottl free(d2); 147171568Sscottl out: 148171568Sscottl free(op->tgtChapDigest); 149171568Sscottl op->tgtChapDigest = NULL; 150171568Sscottl 151171568Sscottl debug(3, "res=%d", res); 152171568Sscottl 153171568Sscottl return res; 154171568Sscottl} 155171568Sscottl 156171568Sscottlstatic void 157171568SscottlprocessParams(isess_t *sess, pdu_t *pp) 158171568Sscottl{ 159171568Sscottl isc_opt_t *op = sess->op; 160171568Sscottl int len, klen, n; 161171568Sscottl char *eq, *ptr; 162171568Sscottl 163171568Sscottl debug_called(3); 164171568Sscottl 165171568Sscottl len = pp->ds_len; 166211095Sdes ptr = (char *)pp->ds_addr; 167171568Sscottl while(len > 0) { 168171568Sscottl if(vflag > 1) 169171568Sscottl printf("got: len=%d %s\n", len, ptr); 170171568Sscottl klen = 0; 171171568Sscottl if((eq = strchr(ptr, '=')) != NULL) 172171568Sscottl klen = eq - ptr; 173171568Sscottl if(klen > 0) { 174171568Sscottl if(strncmp(ptr, "TargetAddress", klen) == 0) { 175185289Sscottl char *p, *q, *ta = NULL; 176171568Sscottl 177171568Sscottl // TargetAddress=domainname[:port][,portal-group-tag] 178171568Sscottl // XXX: if(op->targetAddress) free(op->targetAddress); 179171568Sscottl q = op->targetAddress = strdup(eq+1); 180171568Sscottl if(*q == '[') { 181171568Sscottl // bracketed IPv6 182185289Sscottl if((q = strchr(q, ']')) != NULL) { 183185289Sscottl *q++ = '\0'; 184185289Sscottl ta = op->targetAddress; 185185289Sscottl op->targetAddress = strdup(ta+1); 186185289Sscottl } else 187171568Sscottl q = op->targetAddress; 188171568Sscottl } 189171568Sscottl if((p = strchr(q, ',')) != NULL) { 190171568Sscottl *p++ = 0; 191171568Sscottl op->targetPortalGroupTag = atoi(p); 192171568Sscottl } 193171568Sscottl if((p = strchr(q, ':')) != NULL) { 194171568Sscottl *p++ = 0; 195171568Sscottl op->port = atoi(p); 196171568Sscottl } 197185289Sscottl if(ta) 198185289Sscottl free(ta); 199171568Sscottl } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) { 200171568Sscottl // danny's RFC 201171568Sscottl op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0); 202171568Sscottl } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) { 203171568Sscottl op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0); 204171568Sscottl } else if(strncmp(ptr, "HeaderDigest", klen) == 0) { 205171568Sscottl op->headerDigest = selectFrom(eq+1, DigestMethods); 206171568Sscottl } else if(strncmp(ptr, "DataDigest", klen) == 0) { 207171568Sscottl op->dataDigest = selectFrom(eq+1, DigestMethods); 208171568Sscottl } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0) 209171568Sscottl op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0); 210171568Sscottl#if 0 211171568Sscottl else 212171568Sscottl for(kp = keyMap; kp->name; kp++) { 213171568Sscottl if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=') 214171568Sscottl mp->func(sess, ptr+kp->len+1, GET); 215171568Sscottl } 216171568Sscottl#endif 217171568Sscottl } 218171568Sscottl n = strlen(ptr) + 1; 219171568Sscottl len -= n; 220171568Sscottl ptr += n; 221171568Sscottl } 222171568Sscottl 223171568Sscottl} 224171568Sscottl 225171568Sscottlstatic int 226171568SscottlhandleLoginResp(isess_t *sess, pdu_t *pp) 227171568Sscottl{ 228171568Sscottl login_rsp_t *lp = (login_rsp_t *)pp; 229171568Sscottl uint st_class, status = ntohs(lp->status); 230171568Sscottl 231171568Sscottl debug_called(3); 232171568Sscottl debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status); 233171568Sscottl 234171568Sscottl st_class = status >> 8; 235171568Sscottl if(status) { 236211095Sdes uint st_detail = status & 0xff; 237171568Sscottl 238171568Sscottl switch(st_class) { 239171568Sscottl case 1: // Redirect 240171568Sscottl switch(st_detail) { 241171568Sscottl // the ITN (iSCSI target Name) requests a: 242171568Sscottl case 1: // temporary address change 243171568Sscottl case 2: // permanent address change 244171568Sscottl status = 0; 245171568Sscottl } 246171568Sscottl break; 247171568Sscottl 248171568Sscottl case 2: // Initiator Error 249171568Sscottl if(st_detail < CLASS1_ERRS) 250171568Sscottl printf("0x%04x: %s\n", status, status_class1[st_detail]); 251171568Sscottl break; 252171568Sscottl 253171568Sscottl case 3: 254171568Sscottl if(st_detail < CLASS3_ERRS) 255171568Sscottl printf("0x%04x: %s\n", status, status_class3[st_detail]); 256171568Sscottl break; 257171568Sscottl } 258171568Sscottl } 259171568Sscottl 260171568Sscottl if(status == 0) { 261171568Sscottl processParams(sess, pp); 262171568Sscottl setOptions(sess, 0); // XXX: just in case ... 263171568Sscottl 264171568Sscottl if(lp->T) { 265171568Sscottl isc_opt_t *op = sess->op; 266171568Sscottl 267171568Sscottl if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL)) 268171568Sscottl if(handleTgtResp(sess, pp) != 0) 269171568Sscottl return 1; // XXX: Authentication failure ... 270171568Sscottl sess->csg = lp->NSG; 271171568Sscottl if(sess->csg == FF_PHASE) { 272171568Sscottl // XXX: will need this when implementing reconnect. 273171568Sscottl sess->tsih = lp->tsih; 274171568Sscottl debug(2, "TSIH=%x", sess->tsih); 275171568Sscottl } 276171568Sscottl } 277171568Sscottl } 278171568Sscottl 279171568Sscottl return st_class; 280171568Sscottl} 281171568Sscottl 282171568Sscottlstatic int 283171568SscottlhandleChap(isess_t *sess, pdu_t *pp) 284171568Sscottl{ 285171568Sscottl pdu_t spp; 286171568Sscottl login_req_t *lp; 287171568Sscottl isc_opt_t *op = sess->op; 288171568Sscottl char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits 289171568Sscottl 290171568Sscottl debug_called(3); 291171568Sscottl 292171568Sscottl bzero(&spp, sizeof(pdu_t)); 293171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 294171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 295171568Sscottl memcpy(lp->isid, sess->isid, 6); 296171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 297171568Sscottl lp->CID = htons(1); 298171568Sscottl lp->CSG = SN_PHASE; // Security Negotiation 299171568Sscottl lp->NSG = LON_PHASE; 300171568Sscottl lp->T = 1; 301171568Sscottl 302171568Sscottl if(((ap = getkeyval("CHAP_A=", pp)) == NULL) || 303171568Sscottl ((ip = getkeyval("CHAP_I=", pp)) == NULL) || 304171568Sscottl ((cp = getkeyval("CHAP_C=", pp)) == NULL)) 305171568Sscottl return -1; 306171568Sscottl 307171568Sscottl if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL) 308171568Sscottl return -1; 309171568Sscottl 310171568Sscottl addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName); 311171568Sscottl addText(&spp, "CHAP_R=%s", digest); 312171568Sscottl free(digest); 313171568Sscottl 314171568Sscottl if(op->tgtChapSecret != NULL) { 315171568Sscottl op->tgtChapID = (random() >> 24) % 255; // should be random enough ... 316171568Sscottl addText(&spp, "CHAP_I=%d", op->tgtChapID); 317171568Sscottl cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8); 318171568Sscottl addText(&spp, "CHAP_C=%s", cp); 319171568Sscottl op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret); 320171568Sscottl } 321171568Sscottl 322171568Sscottl return sendPDU(sess, &spp, handleLoginResp); 323171568Sscottl} 324171568Sscottl 325171568Sscottlstatic int 326171568Sscottlauthenticate(isess_t *sess) 327171568Sscottl{ 328171568Sscottl pdu_t spp; 329171568Sscottl login_req_t *lp; 330171568Sscottl isc_opt_t *op = sess->op; 331171568Sscottl 332171568Sscottl bzero(&spp, sizeof(pdu_t)); 333171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 334171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 335171568Sscottl memcpy(lp->isid, sess->isid, 6); 336171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 337171568Sscottl lp->CID = htons(1); 338171568Sscottl lp->CSG = SN_PHASE; // Security Negotiation 339171568Sscottl lp->NSG = SN_PHASE; 340171568Sscottl lp->T = 0; 341171568Sscottl 342171568Sscottl switch((authm_t)lookup(AuthMethods, op->authMethod)) { 343171568Sscottl case NONE: 344171568Sscottl return 0; 345171568Sscottl 346171568Sscottl case KRB5: 347171568Sscottl case SPKM1: 348171568Sscottl case SPKM2: 349171568Sscottl case SRP: 350171568Sscottl return 2; 351171568Sscottl 352171568Sscottl case CHAP: 353171568Sscottl if(op->chapDigest == 0) 354171568Sscottl addText(&spp, "CHAP_A=5"); 355171568Sscottl else 356171568Sscottl if(strcmp(op->chapDigest, "MD5") == 0) 357171568Sscottl addText(&spp, "CHAP_A=5"); 358171568Sscottl else 359171568Sscottl if(strcmp(op->chapDigest, "SHA1") == 0) 360171568Sscottl addText(&spp, "CHAP_A=7"); 361171568Sscottl else 362171568Sscottl addText(&spp, "CHAP_A=5,7"); 363171568Sscottl return sendPDU(sess, &spp, handleChap); 364171568Sscottl } 365171568Sscottl return 1; 366171568Sscottl} 367171568Sscottl 368171568Sscottlint 369171568SscottlloginPhase(isess_t *sess) 370171568Sscottl{ 371171568Sscottl pdu_t spp, *sp = &spp; 372171568Sscottl isc_opt_t *op = sess->op; 373171568Sscottl login_req_t *lp; 374171568Sscottl int status = 1; 375171568Sscottl 376171568Sscottl debug_called(3); 377171568Sscottl 378171568Sscottl bzero(sp, sizeof(pdu_t)); 379171568Sscottl lp = (login_req_t *)&spp.ipdu.bhs; 380171568Sscottl lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 381171568Sscottl memcpy(lp->isid, sess->isid, 6); 382171568Sscottl lp->tsih = sess->tsih; // MUST be zero the first time! 383171568Sscottl lp->CID = htons(1); // sess->cid? 384171568Sscottl 385171568Sscottl if((lp->CSG = sess->csg) == LON_PHASE) 386171568Sscottl lp->NSG = FF_PHASE; // lets try and go full feature ... 387171568Sscottl else 388171568Sscottl lp->NSG = LON_PHASE; 389171568Sscottl lp->T = 1; // transit to next login stage 390171568Sscottl 391171568Sscottl if(sess->flags & SESS_INITIALLOGIN1) { 392171568Sscottl sess->flags &= ~SESS_INITIALLOGIN1; 393171568Sscottl 394171568Sscottl addText(sp, "SessionType=%s", op->sessionType); 395171568Sscottl addText(sp, "InitiatorName=%s", op->initiatorName); 396171568Sscottl if(strcmp(op->sessionType, "Discovery") != 0) { 397171568Sscottl addText(sp, "TargetName=%s", op->targetName); 398171568Sscottl } 399171568Sscottl } 400171568Sscottl switch(sess->csg) { 401171568Sscottl case SN_PHASE: // Security Negotiation 402171568Sscottl addText(sp, "AuthMethod=%s", op->authMethod); 403171568Sscottl break; 404171568Sscottl 405171568Sscottl case LON_PHASE: // Login Operational Negotiation 406171568Sscottl if((sess->flags & SESS_NEGODONE) == 0) { 407171568Sscottl sess->flags |= SESS_NEGODONE; 408171568Sscottl addText(sp, "MaxBurstLength=%d", op->maxBurstLength); 409171568Sscottl addText(sp, "HeaderDigest=%s", op->headerDigest); 410171568Sscottl addText(sp, "DataDigest=%s", op->dataDigest); 411171568Sscottl addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength); 412171568Sscottl addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel); 413171568Sscottl addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait); 414171568Sscottl addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain); 415171568Sscottl addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No"); 416171568Sscottl addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No"); 417171568Sscottl addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T); 418171568Sscottl 419171568Sscottl if(strcmp(op->sessionType, "Discovery") != 0) { 420171568Sscottl addText(sp, "MaxConnections=%d", op->maxConnections); 421171568Sscottl addText(sp, "FirstBurstLength=%d", op->firstBurstLength); 422171568Sscottl addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No"); 423171568Sscottl addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No"); 424171568Sscottl } 425171568Sscottl } 426171568Sscottl 427171568Sscottl break; 428171568Sscottl } 429171568Sscottl 430171568Sscottl status = sendPDU(sess, &spp, handleLoginResp); 431171568Sscottl 432171568Sscottl switch(status) { 433171568Sscottl case 0: // all is ok ... 434171568Sscottl if(sess->csg == SN_PHASE) 435171568Sscottl /* 436171568Sscottl | if we are still here, then we need 437171568Sscottl | to exchange some secrets ... 438171568Sscottl */ 439171568Sscottl status = authenticate(sess); 440171568Sscottl } 441171568Sscottl 442171568Sscottl return status; 443171568Sscottl} 444