PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/sbin/iscontrol/login.c

https://gitlab.com/tlevine/DragonFlyBSD
C | 437 lines | 341 code | 57 blank | 39 comment | 94 complexity | 5fa58b9071eff59376ac8127cfc45494 MD5 | raw file
  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. #include <sys/param.h>
  31. #include <sys/types.h>
  32. #include <sys/socket.h>
  33. #include <sys/sysctl.h>
  34. #include <netinet/in.h>
  35. #include <netinet/tcp.h>
  36. #include <arpa/inet.h>
  37. #include <sys/ioctl.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include "iscsi.h"
  42. #include "iscontrol.h"
  43. static char *status_class1[] = {
  44. "Initiator error",
  45. "Authentication failure",
  46. "Authorization failure",
  47. "Not found",
  48. "Target removed",
  49. "Unsupported version",
  50. "Too many connections",
  51. "Missing parameter",
  52. "Can't include in session",
  53. "Session type not suported",
  54. "Session does not exist",
  55. "Invalid during login",
  56. };
  57. #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
  58. static char *status_class3[] = {
  59. "Target error",
  60. "Service unavailable",
  61. "Out of resources"
  62. };
  63. #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
  64. static char *
  65. selectFrom(char *str, token_t *list)
  66. {
  67. char *sep, *sp;
  68. token_t *lp;
  69. int n;
  70. sp = str;
  71. do {
  72. sep = strchr(sp, ',');
  73. if(sep != NULL)
  74. n = sep - sp;
  75. else
  76. n = strlen(sp);
  77. for(lp = list; lp->name != NULL; lp++) {
  78. if(strncasecmp(lp->name, sp, n) == 0)
  79. return strdup(lp->name);
  80. }
  81. sp = sep + 1;
  82. } while(sep != NULL);
  83. return NULL;
  84. }
  85. static char *
  86. getkeyval(char *key, pdu_t *pp)
  87. {
  88. char *ptr;
  89. int klen, len, n;
  90. debug_called(3);
  91. len = pp->ds_len;
  92. ptr = (char *)pp->ds;
  93. klen = strlen(key);
  94. while(len > klen) {
  95. if(strncmp(key, ptr, klen) == 0)
  96. return ptr+klen;
  97. n = strlen(ptr) + 1;
  98. len -= n;
  99. ptr += n;
  100. }
  101. return 0;
  102. }
  103. static int
  104. handleTgtResp(isess_t *sess, pdu_t *pp)
  105. {
  106. isc_opt_t *op = sess->op;
  107. char *np, *rp, *d1, *d2;
  108. int res, l1, l2;
  109. res = -1;
  110. if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
  111. ((rp = getkeyval("CHAP_R=", pp)) == NULL))
  112. goto out;
  113. if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
  114. fprintf(stderr, "%s does not match\n", np);
  115. goto out;
  116. }
  117. l1 = str2bin(op->tgtChapDigest, &d1);
  118. l2 = str2bin(rp, &d2);
  119. debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
  120. if(l1 == l2 && memcmp(d1, d2, l1) == 0)
  121. res = 0;
  122. if(l1)
  123. free(d1);
  124. if(l2)
  125. free(d2);
  126. out:
  127. free(op->tgtChapDigest);
  128. op->tgtChapDigest = NULL;
  129. debug(3, "res=%d", res);
  130. return res;
  131. }
  132. static void
  133. processParams(isess_t *sess, pdu_t *pp)
  134. {
  135. isc_opt_t *op = sess->op;
  136. int len, klen, n;
  137. char *eq, *ptr;
  138. debug_called(3);
  139. len = pp->ds_len;
  140. ptr = (char *)pp->ds;
  141. while(len > 0) {
  142. if(vflag > 1)
  143. printf("got: len=%d %s\n", len, ptr);
  144. klen = 0;
  145. if((eq = strchr(ptr, '=')) != NULL)
  146. klen = eq - ptr;
  147. if(klen > 0) {
  148. if(strncmp(ptr, "TargetAddress", klen) == 0) {
  149. char *p, *q, *ta = NULL;
  150. // TargetAddress=domainname[:port][,portal-group-tag]
  151. // XXX: if(op->targetAddress) free(op->targetAddress);
  152. q = op->targetAddress = strdup(eq+1);
  153. if(*q == '[') {
  154. // bracketed IPv6
  155. if((q = strchr(q, ']')) != NULL) {
  156. *q++ = '\0';
  157. ta = op->targetAddress;
  158. op->targetAddress = strdup(ta+1);
  159. } else
  160. q = op->targetAddress;
  161. }
  162. if((p = strchr(q, ',')) != NULL) {
  163. *p++ = 0;
  164. op->targetPortalGroupTag = atoi(p);
  165. }
  166. if((p = strchr(q, ':')) != NULL) {
  167. *p++ = 0;
  168. op->port = atoi(p);
  169. }
  170. if(ta)
  171. free(ta);
  172. } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
  173. // danny's RFC
  174. op->maxXmitDataSegmentLength = strtol(eq+1, NULL, 0);
  175. } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
  176. op->targetPortalGroupTag = strtol(eq+1, NULL, 0);
  177. } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
  178. op->headerDigest = selectFrom(eq+1, DigestMethods);
  179. } else if(strncmp(ptr, "DataDigest", klen) == 0) {
  180. op->dataDigest = selectFrom(eq+1, DigestMethods);
  181. } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
  182. op->maxOutstandingR2T = strtol(eq+1, NULL, 0);
  183. #if 0
  184. else
  185. for(kp = keyMap; kp->name; kp++) {
  186. if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
  187. mp->func(sess, ptr+kp->len+1, GET);
  188. }
  189. #endif
  190. }
  191. n = strlen(ptr) + 1;
  192. len -= n;
  193. ptr += n;
  194. }
  195. }
  196. static int
  197. handleLoginResp(isess_t *sess, pdu_t *pp)
  198. {
  199. login_rsp_t *lp = (login_rsp_t *)pp;
  200. uint st_class, status = ntohs(lp->status);
  201. debug_called(3);
  202. debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
  203. st_class = status >> 8;
  204. if(status) {
  205. unsigned int st_detail = status & 0xff;
  206. switch(st_class) {
  207. case 1: // Redirect
  208. switch(st_detail) {
  209. // the ITN (iSCSI target Name) requests a:
  210. case 1: // temporary address change
  211. case 2: // permanent address change
  212. status = 0;
  213. }
  214. break;
  215. case 2: // Initiator Error
  216. if(st_detail < CLASS1_ERRS)
  217. printf("0x%04x: %s\n", status, status_class1[st_detail]);
  218. break;
  219. case 3:
  220. if(st_detail < CLASS3_ERRS)
  221. printf("0x%04x: %s\n", status, status_class3[st_detail]);
  222. break;
  223. }
  224. }
  225. if(status == 0) {
  226. processParams(sess, pp);
  227. setOptions(sess, 0); // XXX: just in case ...
  228. if(lp->T) {
  229. isc_opt_t *op = sess->op;
  230. if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
  231. if(handleTgtResp(sess, pp) != 0)
  232. return 1; // XXX: Authentication failure ...
  233. sess->csg = lp->NSG;
  234. if(sess->csg == FF_PHASE) {
  235. // XXX: will need this when implementing reconnect.
  236. sess->tsih = lp->tsih;
  237. debug(2, "TSIH=%x", sess->tsih);
  238. }
  239. }
  240. }
  241. return st_class;
  242. }
  243. static int
  244. handleChap(isess_t *sess, pdu_t *pp)
  245. {
  246. pdu_t spp;
  247. login_req_t *lp;
  248. isc_opt_t *op = sess->op;
  249. char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
  250. debug_called(3);
  251. bzero(&spp, sizeof(pdu_t));
  252. lp = (login_req_t *)&spp.ipdu.bhs;
  253. lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
  254. memcpy(lp->isid, sess->isid, 6);
  255. lp->tsih = sess->tsih; // MUST be zero the first time!
  256. lp->CID = htons(1);
  257. lp->CSG = SN_PHASE; // Security Negotiation
  258. lp->NSG = LON_PHASE;
  259. lp->T = 1;
  260. if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
  261. ((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
  262. ((cp = getkeyval("CHAP_C=", pp)) == NULL))
  263. return -1;
  264. if((digest = chapDigest(ap, (char)strtol(ip, NULL, 0), cp, op->chapSecret)) == NULL)
  265. return -1;
  266. addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
  267. addText(&spp, "CHAP_R=%s", digest);
  268. free(digest);
  269. if(op->tgtChapSecret != NULL) {
  270. op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
  271. addText(&spp, "CHAP_I=%d", op->tgtChapID);
  272. cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
  273. addText(&spp, "CHAP_C=%s", cp);
  274. op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
  275. }
  276. return sendPDU(sess, &spp, handleLoginResp);
  277. }
  278. static int
  279. authenticate(isess_t *sess)
  280. {
  281. pdu_t spp;
  282. login_req_t *lp;
  283. isc_opt_t *op = sess->op;
  284. bzero(&spp, sizeof(pdu_t));
  285. lp = (login_req_t *)&spp.ipdu.bhs;
  286. lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
  287. memcpy(lp->isid, sess->isid, 6);
  288. lp->tsih = sess->tsih; // MUST be zero the first time!
  289. lp->CID = htons(1);
  290. lp->CSG = SN_PHASE; // Security Negotiation
  291. lp->NSG = SN_PHASE;
  292. lp->T = 0;
  293. switch((authm_t)lookup(AuthMethods, op->authMethod)) {
  294. case NONE:
  295. return 0;
  296. case KRB5:
  297. case SPKM1:
  298. case SPKM2:
  299. case SRP:
  300. return 2;
  301. case CHAP:
  302. if(op->chapDigest == 0)
  303. addText(&spp, "CHAP_A=5");
  304. else
  305. if(strcmp(op->chapDigest, "MD5") == 0)
  306. addText(&spp, "CHAP_A=5");
  307. else
  308. if(strcmp(op->chapDigest, "SHA1") == 0)
  309. addText(&spp, "CHAP_A=7");
  310. else
  311. addText(&spp, "CHAP_A=5,7");
  312. return sendPDU(sess, &spp, handleChap);
  313. }
  314. return 1;
  315. }
  316. int
  317. loginPhase(isess_t *sess)
  318. {
  319. pdu_t spp, *sp = &spp;
  320. isc_opt_t *op = sess->op;
  321. login_req_t *lp;
  322. int status = 1;
  323. debug_called(3);
  324. bzero(sp, sizeof(pdu_t));
  325. lp = (login_req_t *)&spp.ipdu.bhs;
  326. lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
  327. memcpy(lp->isid, sess->isid, 6);
  328. lp->tsih = sess->tsih; // MUST be zero the first time!
  329. lp->CID = htons(1); // sess->cid?
  330. if((lp->CSG = sess->csg) == LON_PHASE)
  331. lp->NSG = FF_PHASE; // lets try and go full feature ...
  332. else
  333. lp->NSG = LON_PHASE;
  334. lp->T = 1; // transit to next login stage
  335. if(sess->flags & SESS_INITIALLOGIN1) {
  336. sess->flags &= ~SESS_INITIALLOGIN1;
  337. addText(sp, "SessionType=%s", op->sessionType);
  338. addText(sp, "InitiatorName=%s", op->initiatorName);
  339. if(strcmp(op->sessionType, "Discovery") != 0) {
  340. addText(sp, "TargetName=%s", op->targetName);
  341. }
  342. }
  343. switch(sess->csg) {
  344. case SN_PHASE: // Security Negotiation
  345. addText(sp, "AuthMethod=%s", op->authMethod);
  346. break;
  347. case LON_PHASE: // Login Operational Negotiation
  348. if((sess->flags & SESS_NEGODONE) == 0) {
  349. sess->flags |= SESS_NEGODONE;
  350. addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
  351. addText(sp, "HeaderDigest=%s", op->headerDigest);
  352. addText(sp, "DataDigest=%s", op->dataDigest);
  353. addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
  354. addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
  355. addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
  356. addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
  357. addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
  358. addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
  359. addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
  360. if(strcmp(op->sessionType, "Discovery") != 0) {
  361. addText(sp, "MaxConnections=%d", op->maxConnections);
  362. addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
  363. addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
  364. addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
  365. }
  366. }
  367. break;
  368. }
  369. status = sendPDU(sess, &spp, handleLoginResp);
  370. switch(status) {
  371. case 0: // all is ok ...
  372. if(sess->csg == SN_PHASE)
  373. /*
  374. | if we are still here, then we need
  375. | to exchange some secrets ...
  376. */
  377. status = authenticate(sess);
  378. }
  379. return status;
  380. }