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