login.c revision 185289
1/*- 2 * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27/* 28 | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $ 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: head/sbin/iscontrol/login.c 185289 2008-11-25 07:17:11Z scottl $"); 33 34#include <sys/param.h> 35#include <sys/types.h> 36#include <sys/socket.h> 37#include <sys/sysctl.h> 38 39#include <netinet/in.h> 40#include <netinet/tcp.h> 41#include <arpa/inet.h> 42#if __FreeBSD_version < 500000 43#include <sys/time.h> 44#endif 45#include <sys/ioctl.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49 50#include "iscsi.h" 51#include "iscontrol.h" 52 53static char *status_class1[] = { 54 "Initiator error", 55 "Authentication failure", 56 "Authorization failure", 57 "Not found", 58 "Target removed", 59 "Unsupported version", 60 "Too many connections", 61 "Missing parameter", 62 "Can't include in session", 63 "Session type not suported", 64 "Session does not exist", 65 "Invalid during login", 66}; 67#define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *)) 68 69static char *status_class3[] = { 70 "Target error", 71 "Service unavailable", 72 "Out of resources" 73}; 74#define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *)) 75 76static char * 77selectFrom(char *str, token_t *list) 78{ 79 char *sep, *sp; 80 token_t *lp; 81 int n; 82 83 sp = str; 84 do { 85 sep = strchr(sp, ','); 86 if(sep != NULL) 87 n = sep - sp; 88 else 89 n = strlen(sp); 90 91 for(lp = list; lp->name != NULL; lp++) { 92 if(strncasecmp(lp->name, sp, n) == 0) 93 return strdup(lp->name); 94 } 95 sp = sep + 1; 96 } while(sep != NULL); 97 98 return NULL; 99} 100 101static char * 102getkeyval(char *key, pdu_t *pp) 103{ 104 char *ptr; 105 int klen, len, n; 106 107 debug_called(3); 108 109 len = pp->ds_len; 110 ptr = (char *)pp->ds; 111 klen = strlen(key); 112 while(len > klen) { 113 if(strncmp(key, ptr, klen) == 0) 114 return ptr+klen; 115 n = strlen(ptr) + 1; 116 len -= n; 117 ptr += n; 118 } 119 return 0; 120} 121 122static int 123handleTgtResp(isess_t *sess, pdu_t *pp) 124{ 125 isc_opt_t *op = sess->op; 126 char *np, *rp, *d1, *d2; 127 int res, l1, l2; 128 129 res = -1; 130 if(((np = getkeyval("CHAP_N=", pp)) == NULL) || 131 ((rp = getkeyval("CHAP_R=", pp)) == NULL)) 132 goto out; 133 if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) { 134 fprintf(stderr, "%s does not match\n", np); 135 goto out; 136 } 137 l1 = str2bin(op->tgtChapDigest, &d1); 138 l2 = str2bin(rp, &d2); 139 140 debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp); 141 if(l1 == l2 && memcmp(d1, d2, l1) == 0) 142 res = 0; 143 if(l1) 144 free(d1); 145 if(l2) 146 free(d2); 147 out: 148 free(op->tgtChapDigest); 149 op->tgtChapDigest = NULL; 150 151 debug(3, "res=%d", res); 152 153 return res; 154} 155 156static void 157processParams(isess_t *sess, pdu_t *pp) 158{ 159 isc_opt_t *op = sess->op; 160 int len, klen, n; 161 char *eq, *ptr; 162 163 debug_called(3); 164 165 len = pp->ds_len; 166 ptr = (char *)pp->ds; 167 while(len > 0) { 168 if(vflag > 1) 169 printf("got: len=%d %s\n", len, ptr); 170 klen = 0; 171 if((eq = strchr(ptr, '=')) != NULL) 172 klen = eq - ptr; 173 if(klen > 0) { 174 if(strncmp(ptr, "TargetAddress", klen) == 0) { 175 char *p, *q, *ta = NULL; 176 177 // TargetAddress=domainname[:port][,portal-group-tag] 178 // XXX: if(op->targetAddress) free(op->targetAddress); 179 q = op->targetAddress = strdup(eq+1); 180 if(*q == '[') { 181 // bracketed IPv6 182 if((q = strchr(q, ']')) != NULL) { 183 *q++ = '\0'; 184 ta = op->targetAddress; 185 op->targetAddress = strdup(ta+1); 186 } else 187 q = op->targetAddress; 188 } 189 if((p = strchr(q, ',')) != NULL) { 190 *p++ = 0; 191 op->targetPortalGroupTag = atoi(p); 192 } 193 if((p = strchr(q, ':')) != NULL) { 194 *p++ = 0; 195 op->port = atoi(p); 196 } 197 if(ta) 198 free(ta); 199 } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) { 200 // danny's RFC 201 op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0); 202 } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) { 203 op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0); 204 } else if(strncmp(ptr, "HeaderDigest", klen) == 0) { 205 op->headerDigest = selectFrom(eq+1, DigestMethods); 206 } else if(strncmp(ptr, "DataDigest", klen) == 0) { 207 op->dataDigest = selectFrom(eq+1, DigestMethods); 208 } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0) 209 op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0); 210#if 0 211 else 212 for(kp = keyMap; kp->name; kp++) { 213 if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=') 214 mp->func(sess, ptr+kp->len+1, GET); 215 } 216#endif 217 } 218 n = strlen(ptr) + 1; 219 len -= n; 220 ptr += n; 221 } 222 223} 224 225static int 226handleLoginResp(isess_t *sess, pdu_t *pp) 227{ 228 login_rsp_t *lp = (login_rsp_t *)pp; 229 uint st_class, status = ntohs(lp->status); 230 231 debug_called(3); 232 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status); 233 234 st_class = status >> 8; 235 if(status) { 236 int st_detail = status & 0xff; 237 238 switch(st_class) { 239 case 1: // Redirect 240 switch(st_detail) { 241 // the ITN (iSCSI target Name) requests a: 242 case 1: // temporary address change 243 case 2: // permanent address change 244 status = 0; 245 } 246 break; 247 248 case 2: // Initiator Error 249 if(st_detail < CLASS1_ERRS) 250 printf("0x%04x: %s\n", status, status_class1[st_detail]); 251 break; 252 253 case 3: 254 if(st_detail < CLASS3_ERRS) 255 printf("0x%04x: %s\n", status, status_class3[st_detail]); 256 break; 257 } 258 } 259 260 if(status == 0) { 261 processParams(sess, pp); 262 setOptions(sess, 0); // XXX: just in case ... 263 264 if(lp->T) { 265 isc_opt_t *op = sess->op; 266 267 if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL)) 268 if(handleTgtResp(sess, pp) != 0) 269 return 1; // XXX: Authentication failure ... 270 sess->csg = lp->NSG; 271 if(sess->csg == FF_PHASE) { 272 // XXX: will need this when implementing reconnect. 273 sess->tsih = lp->tsih; 274 debug(2, "TSIH=%x", sess->tsih); 275 } 276 } 277 } 278 279 return st_class; 280} 281 282static int 283handleChap(isess_t *sess, pdu_t *pp) 284{ 285 pdu_t spp; 286 login_req_t *lp; 287 isc_opt_t *op = sess->op; 288 char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits 289 290 debug_called(3); 291 292 bzero(&spp, sizeof(pdu_t)); 293 lp = (login_req_t *)&spp.ipdu.bhs; 294 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 295 memcpy(lp->isid, sess->isid, 6); 296 lp->tsih = sess->tsih; // MUST be zero the first time! 297 lp->CID = htons(1); 298 lp->CSG = SN_PHASE; // Security Negotiation 299 lp->NSG = LON_PHASE; 300 lp->T = 1; 301 302 if(((ap = getkeyval("CHAP_A=", pp)) == NULL) || 303 ((ip = getkeyval("CHAP_I=", pp)) == NULL) || 304 ((cp = getkeyval("CHAP_C=", pp)) == NULL)) 305 return -1; 306 307 if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL) 308 return -1; 309 310 addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName); 311 addText(&spp, "CHAP_R=%s", digest); 312 free(digest); 313 314 if(op->tgtChapSecret != NULL) { 315 op->tgtChapID = (random() >> 24) % 255; // should be random enough ... 316 addText(&spp, "CHAP_I=%d", op->tgtChapID); 317 cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8); 318 addText(&spp, "CHAP_C=%s", cp); 319 op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret); 320 } 321 322 return sendPDU(sess, &spp, handleLoginResp); 323} 324 325static int 326authenticate(isess_t *sess) 327{ 328 pdu_t spp; 329 login_req_t *lp; 330 isc_opt_t *op = sess->op; 331 332 bzero(&spp, sizeof(pdu_t)); 333 lp = (login_req_t *)&spp.ipdu.bhs; 334 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 335 memcpy(lp->isid, sess->isid, 6); 336 lp->tsih = sess->tsih; // MUST be zero the first time! 337 lp->CID = htons(1); 338 lp->CSG = SN_PHASE; // Security Negotiation 339 lp->NSG = SN_PHASE; 340 lp->T = 0; 341 342 switch((authm_t)lookup(AuthMethods, op->authMethod)) { 343 case NONE: 344 return 0; 345 346 case KRB5: 347 case SPKM1: 348 case SPKM2: 349 case SRP: 350 return 2; 351 352 case CHAP: 353 if(op->chapDigest == 0) 354 addText(&spp, "CHAP_A=5"); 355 else 356 if(strcmp(op->chapDigest, "MD5") == 0) 357 addText(&spp, "CHAP_A=5"); 358 else 359 if(strcmp(op->chapDigest, "SHA1") == 0) 360 addText(&spp, "CHAP_A=7"); 361 else 362 addText(&spp, "CHAP_A=5,7"); 363 return sendPDU(sess, &spp, handleChap); 364 } 365 return 1; 366} 367 368int 369loginPhase(isess_t *sess) 370{ 371 pdu_t spp, *sp = &spp; 372 isc_opt_t *op = sess->op; 373 login_req_t *lp; 374 int status = 1; 375 376 debug_called(3); 377 378 bzero(sp, sizeof(pdu_t)); 379 lp = (login_req_t *)&spp.ipdu.bhs; 380 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate 381 memcpy(lp->isid, sess->isid, 6); 382 lp->tsih = sess->tsih; // MUST be zero the first time! 383 lp->CID = htons(1); // sess->cid? 384 385 if((lp->CSG = sess->csg) == LON_PHASE) 386 lp->NSG = FF_PHASE; // lets try and go full feature ... 387 else 388 lp->NSG = LON_PHASE; 389 lp->T = 1; // transit to next login stage 390 391 if(sess->flags & SESS_INITIALLOGIN1) { 392 sess->flags &= ~SESS_INITIALLOGIN1; 393 394 addText(sp, "SessionType=%s", op->sessionType); 395 addText(sp, "InitiatorName=%s", op->initiatorName); 396 if(strcmp(op->sessionType, "Discovery") != 0) { 397 addText(sp, "TargetName=%s", op->targetName); 398 } 399 } 400 switch(sess->csg) { 401 case SN_PHASE: // Security Negotiation 402 addText(sp, "AuthMethod=%s", op->authMethod); 403 break; 404 405 case LON_PHASE: // Login Operational Negotiation 406 if((sess->flags & SESS_NEGODONE) == 0) { 407 sess->flags |= SESS_NEGODONE; 408 addText(sp, "MaxBurstLength=%d", op->maxBurstLength); 409 addText(sp, "HeaderDigest=%s", op->headerDigest); 410 addText(sp, "DataDigest=%s", op->dataDigest); 411 addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength); 412 addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel); 413 addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait); 414 addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain); 415 addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No"); 416 addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No"); 417 addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T); 418 419 if(strcmp(op->sessionType, "Discovery") != 0) { 420 addText(sp, "MaxConnections=%d", op->maxConnections); 421 addText(sp, "FirstBurstLength=%d", op->firstBurstLength); 422 addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No"); 423 addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No"); 424 } 425 } 426 427 break; 428 } 429 430 status = sendPDU(sess, &spp, handleLoginResp); 431 432 switch(status) { 433 case 0: // all is ok ... 434 if(sess->csg == SN_PHASE) 435 /* 436 | if we are still here, then we need 437 | to exchange some secrets ... 438 */ 439 status = authenticate(sess); 440 } 441 442 return status; 443} 444