atomic.h: use system namespace for function arguments
[dragonfly.git] / sbin / iscontrol / login.c
blobcf8f1e9b19c4ed80240c2d11df63f2ebb7d68a68
1 /*-
2 * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
3 * All rights reserved.
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.
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.
28 | $Id: login.c,v 1.4 2007/04/27 07:40:40 danny Exp danny $
31 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/sysctl.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <arpa/inet.h>
41 #if __FreeBSD_version < 500000
42 #include <sys/time.h>
43 #endif
44 #include <sys/ioctl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
49 #include "iscsi.h"
50 #include "iscontrol.h"
52 static char *status_class1[] = {
53 "Initiator error",
54 "Authentication failure",
55 "Authorization failure",
56 "Not found",
57 "Target removed",
58 "Unsupported version",
59 "Too many connections",
60 "Missing parameter",
61 "Can't include in session",
62 "Session type not suported",
63 "Session does not exist",
64 "Invalid during login",
66 #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
68 static char *status_class3[] = {
69 "Target error",
70 "Service unavailable",
71 "Out of resources"
73 #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
75 static char *
76 selectFrom(char *str, token_t *list)
78 char *sep, *sp;
79 token_t *lp;
80 int n;
82 sp = str;
83 do {
84 sep = strchr(sp, ',');
85 if(sep != NULL)
86 n = sep - sp;
87 else
88 n = strlen(sp);
90 for(lp = list; lp->name != NULL; lp++) {
91 if(strncasecmp(lp->name, sp, n) == 0)
92 return strdup(lp->name);
94 sp = sep + 1;
95 } while(sep != NULL);
97 return NULL;
100 static char *
101 getkeyval(char *key, pdu_t *pp)
103 char *ptr;
104 int klen, len, n;
106 debug_called(3);
108 len = pp->ds_len;
109 ptr = (char *)pp->ds;
110 klen = strlen(key);
111 while(len > klen) {
112 if(strncmp(key, ptr, klen) == 0)
113 return ptr+klen;
114 n = strlen(ptr) + 1;
115 len -= n;
116 ptr += n;
118 return 0;
121 static int
122 handleTgtResp(isess_t *sess, pdu_t *pp)
124 isc_opt_t *op = sess->op;
125 char *np, *rp, *d1, *d2;
126 int res, l1, l2;
128 res = -1;
129 if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
130 ((rp = getkeyval("CHAP_R=", pp)) == NULL))
131 goto out;
132 if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
133 fprintf(stderr, "%s does not match\n", np);
134 goto out;
136 l1 = str2bin(op->tgtChapDigest, &d1);
137 l2 = str2bin(rp, &d2);
139 debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
140 if(l1 == l2 && memcmp(d1, d2, l1) == 0)
141 res = 0;
142 if(l1)
143 free(d1);
144 if(l2)
145 free(d2);
146 out:
147 free(op->tgtChapDigest);
148 op->tgtChapDigest = NULL;
150 debug(3, "res=%d", res);
152 return res;
155 static void
156 processParams(isess_t *sess, pdu_t *pp)
158 isc_opt_t *op = sess->op;
159 int len, klen, n;
160 char *eq, *ptr;
162 debug_called(3);
164 len = pp->ds_len;
165 ptr = (char *)pp->ds;
166 while(len > 0) {
167 if(vflag > 1)
168 printf("got: len=%d %s\n", len, ptr);
169 klen = 0;
170 if((eq = strchr(ptr, '=')) != NULL)
171 klen = eq - ptr;
172 if(klen > 0) {
173 if(strncmp(ptr, "TargetAddress", klen) == 0) {
174 char *p, *q, *ta = NULL;
176 // TargetAddress=domainname[:port][,portal-group-tag]
177 // XXX: if(op->targetAddress) free(op->targetAddress);
178 q = op->targetAddress = strdup(eq+1);
179 if(*q == '[') {
180 // bracketed IPv6
181 if((q = strchr(q, ']')) != NULL) {
182 *q++ = '\0';
183 ta = op->targetAddress;
184 op->targetAddress = strdup(ta+1);
185 } else
186 q = op->targetAddress;
188 if((p = strchr(q, ',')) != NULL) {
189 *p++ = 0;
190 op->targetPortalGroupTag = atoi(p);
192 if((p = strchr(q, ':')) != NULL) {
193 *p++ = 0;
194 op->port = atoi(p);
196 if(ta)
197 free(ta);
198 } else if(strncmp(ptr, "MaxRecvDataSegmentLength", klen) == 0) {
199 // danny's RFC
200 op->maxXmitDataSegmentLength = strtol(eq+1, (char **)NULL, 0);
201 } else if(strncmp(ptr, "TargetPortalGroupTag", klen) == 0) {
202 op->targetPortalGroupTag = strtol(eq+1, (char **)NULL, 0);
203 } else if(strncmp(ptr, "HeaderDigest", klen) == 0) {
204 op->headerDigest = selectFrom(eq+1, DigestMethods);
205 } else if(strncmp(ptr, "DataDigest", klen) == 0) {
206 op->dataDigest = selectFrom(eq+1, DigestMethods);
207 } else if(strncmp(ptr, "MaxOutstandingR2T", klen) == 0)
208 op->maxOutstandingR2T = strtol(eq+1, (char **)NULL, 0);
209 #if 0
210 else
211 for(kp = keyMap; kp->name; kp++) {
212 if(strncmp(ptr, kp->name, kp->len) == 0 && ptr[kp->len] == '=')
213 mp->func(sess, ptr+kp->len+1, GET);
215 #endif
217 n = strlen(ptr) + 1;
218 len -= n;
219 ptr += n;
224 static int
225 handleLoginResp(isess_t *sess, pdu_t *pp)
227 login_rsp_t *lp = (login_rsp_t *)pp;
228 uint st_class, status = ntohs(lp->status);
230 debug_called(3);
231 debug(4, "Tbit=%d csg=%d nsg=%d status=%x", lp->T, lp->CSG, lp->NSG, status);
233 st_class = status >> 8;
234 if(status) {
235 unsigned int st_detail = status & 0xff;
237 switch(st_class) {
238 case 1: // Redirect
239 switch(st_detail) {
240 // the ITN (iSCSI target Name) requests a:
241 case 1: // temporary address change
242 case 2: // permanent address change
243 status = 0;
245 break;
247 case 2: // Initiator Error
248 if(st_detail < CLASS1_ERRS)
249 printf("0x%04x: %s\n", status, status_class1[st_detail]);
250 break;
252 case 3:
253 if(st_detail < CLASS3_ERRS)
254 printf("0x%04x: %s\n", status, status_class3[st_detail]);
255 break;
259 if(status == 0) {
260 processParams(sess, pp);
261 setOptions(sess, 0); // XXX: just in case ...
263 if(lp->T) {
264 isc_opt_t *op = sess->op;
266 if(sess->csg == SN_PHASE && (op->tgtChapDigest != NULL))
267 if(handleTgtResp(sess, pp) != 0)
268 return 1; // XXX: Authentication failure ...
269 sess->csg = lp->NSG;
270 if(sess->csg == FF_PHASE) {
271 // XXX: will need this when implementing reconnect.
272 sess->tsih = lp->tsih;
273 debug(2, "TSIH=%x", sess->tsih);
278 return st_class;
281 static int
282 handleChap(isess_t *sess, pdu_t *pp)
284 pdu_t spp;
285 login_req_t *lp;
286 isc_opt_t *op = sess->op;
287 char *ap, *ip, *cp, *digest; // MD5 is 128bits, SHA1 160bits
289 debug_called(3);
291 bzero(&spp, sizeof(pdu_t));
292 lp = (login_req_t *)&spp.ipdu.bhs;
293 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
294 memcpy(lp->isid, sess->isid, 6);
295 lp->tsih = sess->tsih; // MUST be zero the first time!
296 lp->CID = htons(1);
297 lp->CSG = SN_PHASE; // Security Negotiation
298 lp->NSG = LON_PHASE;
299 lp->T = 1;
301 if(((ap = getkeyval("CHAP_A=", pp)) == NULL) ||
302 ((ip = getkeyval("CHAP_I=", pp)) == NULL) ||
303 ((cp = getkeyval("CHAP_C=", pp)) == NULL))
304 return -1;
306 if((digest = chapDigest(ap, (char)strtol(ip, (char **)NULL, 0), cp, op->chapSecret)) == NULL)
307 return -1;
309 addText(&spp, "CHAP_N=%s", op->chapIName? op->chapIName: op->initiatorName);
310 addText(&spp, "CHAP_R=%s", digest);
311 free(digest);
313 if(op->tgtChapSecret != NULL) {
314 op->tgtChapID = (random() >> 24) % 255; // should be random enough ...
315 addText(&spp, "CHAP_I=%d", op->tgtChapID);
316 cp = genChapChallenge(cp, op->tgtChallengeLen? op->tgtChallengeLen: 8);
317 addText(&spp, "CHAP_C=%s", cp);
318 op->tgtChapDigest = chapDigest(ap, op->tgtChapID, cp, op->tgtChapSecret);
321 return sendPDU(sess, &spp, handleLoginResp);
324 static int
325 authenticate(isess_t *sess)
327 pdu_t spp;
328 login_req_t *lp;
329 isc_opt_t *op = sess->op;
331 bzero(&spp, sizeof(pdu_t));
332 lp = (login_req_t *)&spp.ipdu.bhs;
333 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
334 memcpy(lp->isid, sess->isid, 6);
335 lp->tsih = sess->tsih; // MUST be zero the first time!
336 lp->CID = htons(1);
337 lp->CSG = SN_PHASE; // Security Negotiation
338 lp->NSG = SN_PHASE;
339 lp->T = 0;
341 switch((authm_t)lookup(AuthMethods, op->authMethod)) {
342 case NONE:
343 return 0;
345 case KRB5:
346 case SPKM1:
347 case SPKM2:
348 case SRP:
349 return 2;
351 case CHAP:
352 if(op->chapDigest == 0)
353 addText(&spp, "CHAP_A=5");
354 else
355 if(strcmp(op->chapDigest, "MD5") == 0)
356 addText(&spp, "CHAP_A=5");
357 else
358 if(strcmp(op->chapDigest, "SHA1") == 0)
359 addText(&spp, "CHAP_A=7");
360 else
361 addText(&spp, "CHAP_A=5,7");
362 return sendPDU(sess, &spp, handleChap);
364 return 1;
368 loginPhase(isess_t *sess)
370 pdu_t spp, *sp = &spp;
371 isc_opt_t *op = sess->op;
372 login_req_t *lp;
373 int status = 1;
375 debug_called(3);
377 bzero(sp, sizeof(pdu_t));
378 lp = (login_req_t *)&spp.ipdu.bhs;
379 lp->cmd = ISCSI_LOGIN_CMD | 0x40; // login request + Inmediate
380 memcpy(lp->isid, sess->isid, 6);
381 lp->tsih = sess->tsih; // MUST be zero the first time!
382 lp->CID = htons(1); // sess->cid?
384 if((lp->CSG = sess->csg) == LON_PHASE)
385 lp->NSG = FF_PHASE; // lets try and go full feature ...
386 else
387 lp->NSG = LON_PHASE;
388 lp->T = 1; // transit to next login stage
390 if(sess->flags & SESS_INITIALLOGIN1) {
391 sess->flags &= ~SESS_INITIALLOGIN1;
393 addText(sp, "SessionType=%s", op->sessionType);
394 addText(sp, "InitiatorName=%s", op->initiatorName);
395 if(strcmp(op->sessionType, "Discovery") != 0) {
396 addText(sp, "TargetName=%s", op->targetName);
399 switch(sess->csg) {
400 case SN_PHASE: // Security Negotiation
401 addText(sp, "AuthMethod=%s", op->authMethod);
402 break;
404 case LON_PHASE: // Login Operational Negotiation
405 if((sess->flags & SESS_NEGODONE) == 0) {
406 sess->flags |= SESS_NEGODONE;
407 addText(sp, "MaxBurstLength=%d", op->maxBurstLength);
408 addText(sp, "HeaderDigest=%s", op->headerDigest);
409 addText(sp, "DataDigest=%s", op->dataDigest);
410 addText(sp, "MaxRecvDataSegmentLength=%d", op->maxRecvDataSegmentLength);
411 addText(sp, "ErrorRecoveryLevel=%d", op->errorRecoveryLevel);
412 addText(sp, "DefaultTime2Wait=%d", op->defaultTime2Wait);
413 addText(sp, "DefaultTime2Retain=%d", op->defaultTime2Retain);
414 addText(sp, "DataPDUInOrder=%s", op->dataPDUInOrder? "Yes": "No");
415 addText(sp, "DataSequenceInOrder=%s", op->dataSequenceInOrder? "Yes": "No");
416 addText(sp, "MaxOutstandingR2T=%d", op->maxOutstandingR2T);
418 if(strcmp(op->sessionType, "Discovery") != 0) {
419 addText(sp, "MaxConnections=%d", op->maxConnections);
420 addText(sp, "FirstBurstLength=%d", op->firstBurstLength);
421 addText(sp, "InitialR2T=%s", op->initialR2T? "Yes": "No");
422 addText(sp, "ImmediateData=%s", op->immediateData? "Yes": "No");
426 break;
429 status = sendPDU(sess, &spp, handleLoginResp);
431 switch(status) {
432 case 0: // all is ok ...
433 if(sess->csg == SN_PHASE)
435 | if we are still here, then we need
436 | to exchange some secrets ...
438 status = authenticate(sess);
441 return status;