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