summaryrefslogtreecommitdiffstats
path: root/sbin/iscontrol/login.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/iscontrol/login.c')
-rw-r--r--sbin/iscontrol/login.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/sbin/iscontrol/login.c b/sbin/iscontrol/login.c
new file mode 100644
index 0000000..44ea889
--- /dev/null
+++ b/sbin/iscontrol/login.c
@@ -0,0 +1,440 @@
+/*-
+ * Copyright (c) 2005 Daniel Braniss <danny@cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#if __FreeBSD_version < 500000
+#include <sys/time.h>
+#endif
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "iscsi.h"
+#include "iscontrol.h"
+#include "pdu.h"
+
+static char *status_class1[] = {
+ "Initiator error",
+ "Authentication failure",
+ "Authorization failure",
+ "Not found",
+ "Target removed",
+ "Unsupported version",
+ "Too many connections",
+ "Missing parameter",
+ "Can't include in session",
+ "Session type not suported",
+ "Session does not exist",
+ "Invalid during login",
+};
+#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
+
+static char *status_class3[] = {
+ "Target error",
+ "Service unavailable",
+ "Out of resources"
+};
+#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
+
+static char *
+selectFrom(char *str, token_t *list)
+{
+ char *sep, *sp;
+ token_t *lp;
+ int n;
+
+ sp = str;
+ do {
+ sep = strchr(sp, ',');
+ if(sep != NULL)
+ n = sep - sp;
+ else
+ n = strlen(sp);
+
+ for(lp = list; lp->name != NULL; lp++) {
+ if(strncasecmp(lp->name, sp, n) == 0)
+ return strdup(lp->name);
+ }
+ sp = sep + 1;
+ } while(sep != NULL);
+
+ return NULL;
+}
+
+static char *
+getkeyval(char *key, pdu_t *pp)
+{
+ char *ptr;
+ int klen, len, n;
+
+ debug_called(3);
+
+ len = pp->ds_len;
+ ptr = (char *)pp->ds;
+ klen = strlen(key);
+ while(len > klen) {
+ if(strncmp(key, ptr, klen) == 0)
+ return ptr+klen;
+ n = strlen(ptr) + 1;
+ len -= n;
+ ptr += n;
+ }
+ return 0;
+}
+
+static int
+handleTgtResp(isess_t *sess, pdu_t *pp)
+{
+ isc_opt_t *op = sess->op;
+ char *np, *rp, *d1, *d2;
+ int res, l1, l2;
+
+ res = -1;
+ if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
+ ((rp = getkeyval("CHAP_R=", pp)) == NULL))
+ goto out;
+ if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
+ fprintf(stderr, "%s does not match\n", np);
+ goto out;
+ }
+ l1 = str2bin(op->tgtChapDigest, &d1);
+ l2 = str2bin(rp, &d2);
+
+ debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
+ if(l1 == l2 && memcmp(d1, d2, l1) == 0)
+ res = 0;
+ if(l1)
+ free(d1);
+ if(l2)
+ free(d2);
+ out:
+ free(op->tgtChapDigest);
+ op->tgtChapDigest = NULL;
+
+ debug(3, "res=%d", res);
+
+ return res;
+}
+
+static void
+processParams(isess_t *sess, pdu_t *pp)
+{
+ isc_opt_t *op = sess->op;
+ int len, klen, n;
+ char *eq, *ptr;
+
+ debug_called(3);
+
+ len = pp->ds_len;
+ ptr = (char *)pp->ds;
+ while(len > 0) {
+ if(vflag > 1)
+ printf("got: len=%d %s\n", len, ptr);
+ klen = 0;
+ if((eq = strchr(ptr, '=')) != NULL)
+ klen = eq - ptr;
+ if(klen > 0) {
+ if(strncmp(ptr, "TargetAddress", klen) == 0) {
+ char *p, *q;
+
+ // TargetAddress=domainname[:port][,portal-group-tag]
+ // XXX: if(op->targetAddress) free(op->targetAddress);
+ q = op->targetAddress = strdup(eq+1);
+ if(*q == '[') {
+ // bracketed IPv6
+ if((q = strchr(q, ']')) != NULL)
+ q++;
+ else
+ q = op->targetAddress;
+ }
+ if((p = strchr(q, ',')) != NULL) {
+ *p++ = 0;
+ op->targetPortalGroupTag = atoi(p);
+ }
+ if((p = strchr(q, ':')) != NULL) {
+ *p++ = 0;
+ op->port = atoi(p);
+ }
+ } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
+ // danny's RFC
+ op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
+ } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
+ op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
+ } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
+ op->headerDigest = selectFrom(eq+1, DigestMethods);
+ } else if(strncmp(ptr, "DataDigest", klen) == 0) {
+ op->dataDigest = selectFrom(eq+1, DigestMethods);
+ } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
+ op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
+#if 0
+ else
+ for(kp = keyMap; kp->name; kp++) {
+ if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
+ mp->func(sess, ptr+kp->len+1, GET);
+ }
+#endif
+ }
+ n = strlen(ptr) + 1;
+ len -= n;
+ ptr += n;
+ }
+
+}
+
+static int
+handleLoginResp(isess_t *sess, pdu_t *pp)
+{
+ login_rsp_t *lp = (login_rsp_t *)pp;
+ uint st_class, status = ntohs(lp->status);
+
+ debug_called(3);
+ debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
+
+ st_class = status >> 8;
+ if(status) {
+ int st_detail = status & 0xff;
+
+ switch(st_class) {
+ case 1: // Redirect
+ switch(st_detail) {
+ // the ITN (iSCSI target Name) requests a:
+ case 1: // temporary address change
+ case 2: // permanent address change
+ status = 0;
+ }
+ break;
+
+ case 2: // Initiator Error
+ if(st_detail < CLASS1_ERRS)
+ printf("0x%04x: %s\n", status, status_class1[st_detail]);
+ break;
+
+ case 3:
+ if(st_detail < CLASS3_ERRS)
+ printf("0x%04x: %s\n", status, status_class3[st_detail]);
+ break;
+ }
+ }
+
+ if(status == 0) {
+ processParams(sess, pp);
+ setOptions(sess, 0); // XXX: just in case ...
+
+ if(lp->T) {
+ isc_opt_t *op = sess->op;
+
+ if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
+ if(handleTgtResp(sess, pp) != 0)
+ return 1; // XXX: Authentication failure ...
+ sess->csg = lp->NSG;
+ if(sess->csg == FF_PHASE) {
+ // XXX: will need this when implementing reconnect.
+ sess->tsih = lp->tsih;
+ debug(2, "TSIH=%x", sess->tsih);
+ }
+ }
+ }
+
+ return st_class;
+}
+
+static int
+handleChap(isess_t *sess, pdu_t *pp)
+{
+ pdu_t spp;
+ login_req_t *lp;
+ isc_opt_t *op = sess->op;
+ char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
+
+ debug_called(3);
+
+ bzero(&spp, sizeof(pdu_t));
+ lp = (login_req_t *)&spp.ipdu.bhs;
+ lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+ memcpy(lp->isid, sess->isid, 6);
+ lp->tsih = sess->tsih; // MUST be zero the first time!
+ lp->CID = htons(1);
+ lp->CSG = SN_PHASE; // Security Negotiation
+ lp->NSG = LON_PHASE;
+ lp->T = 1;
+
+ if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
+ ((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
+ ((cp = getkeyval("CHAP_C=", pp)) == NULL))
+ return -1;
+
+ if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
+ return -1;
+
+ addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
+ addText(&spp, "CHAP_R=%s", digest);
+ free(digest);
+
+ if(op->tgtChapSecret != NULL) {
+ op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
+ addText(&spp, "CHAP_I=%d", op->tgtChapID);
+ cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
+ addText(&spp, "CHAP_C=%s", cp);
+ op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
+ }
+
+ return sendPDU(sess, &spp, handleLoginResp);
+}
+
+static int
+authenticate(isess_t *sess)
+{
+ pdu_t spp;
+ login_req_t *lp;
+ isc_opt_t *op = sess->op;
+
+ bzero(&spp, sizeof(pdu_t));
+ lp = (login_req_t *)&spp.ipdu.bhs;
+ lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+ memcpy(lp->isid, sess->isid, 6);
+ lp->tsih = sess->tsih; // MUST be zero the first time!
+ lp->CID = htons(1);
+ lp->CSG = SN_PHASE; // Security Negotiation
+ lp->NSG = SN_PHASE;
+ lp->T = 0;
+
+ switch((authm_t)lookup(AuthMethods, op->authMethod)) {
+ case NONE:
+ return 0;
+
+ case KRB5:
+ case SPKM1:
+ case SPKM2:
+ case SRP:
+ return 2;
+
+ case CHAP:
+ if(op->chapDigest == 0)
+ addText(&spp, "CHAP_A=5");
+ else
+ if(strcmp(op->chapDigest, "MD5") == 0)
+ addText(&spp, "CHAP_A=5");
+ else
+ if(strcmp(op->chapDigest, "SHA1") == 0)
+ addText(&spp, "CHAP_A=7");
+ else
+ addText(&spp, "CHAP_A=5,7");
+ return sendPDU(sess, &spp, handleChap);
+ }
+ return 1;
+}
+
+int
+loginPhase(isess_t *sess)
+{
+ pdu_t spp, *sp = &spp;
+ isc_opt_t *op = sess->op;
+ login_req_t *lp;
+ int status = 1;
+
+ debug_called(3);
+
+ bzero(sp, sizeof(pdu_t));
+ lp = (login_req_t *)&spp.ipdu.bhs;
+ lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
+ memcpy(lp->isid, sess->isid, 6);
+ lp->tsih = sess->tsih; // MUST be zero the first time!
+ lp->CID = htons(1); // sess->cid?
+
+ if((lp->CSG = sess->csg) == LON_PHASE)
+ lp->NSG = FF_PHASE; // lets try and go full feature ...
+ else
+ lp->NSG = LON_PHASE;
+ lp->T = 1; // transit to next login stage
+
+ if(sess->flags & SESS_INITIALLOGIN1) {
+ sess->flags &= ~SESS_INITIALLOGIN1;
+
+ addText(sp, "SessionType=%s", op->sessionType);
+ addText(sp, "InitiatorName=%s", op->initiatorName);
+ if(strcmp(op->sessionType, "Discovery") != 0) {
+ addText(sp, "TargetName=%s", op->targetName);
+ }
+ }
+ switch(sess->csg) {
+ case SN_PHASE: // Security Negotiation
+ addText(sp, "AuthMethod=%s", op->authMethod);
+ break;
+
+ case LON_PHASE: // Login Operational Negotiation
+ if((sess->flags & SESS_NEGODONE) == 0) {
+ sess->flags |= SESS_NEGODONE;
+ addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
+ addText(sp, "HeaderDigest=%s", op->headerDigest);
+ addText(sp, "DataDigest=%s", op->dataDigest);
+ addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
+ addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
+ addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
+ addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
+ addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
+ addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
+ addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
+
+ if(strcmp(op->sessionType, "Discovery") != 0) {
+ addText(sp, "MaxConnections=%d", op->maxConnections);
+ addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
+ addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
+ addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
+ }
+ }
+
+ break;
+ }
+
+ status = sendPDU(sess, &spp, handleLoginResp);
+
+ switch(status) {
+ case 0: // all is ok ...
+ if(sess->csg == SN_PHASE)
+ /*
+ | if we are still here, then we need
+ | to exchange some secrets ...
+ */
+ status = authenticate(sess);
+ }
+
+ return status;
+}
OpenPOWER on IntegriCloud