]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sbin/iscontrol/login.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sbin / iscontrol / login.c
1 /*-
2  * Copyright (c) 2005-2010 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 #include <sys/ioctl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <dev/iscsi_initiator/iscsi.h>
48 #include "iscontrol.h"
49
50 static char *status_class1[] = {
51      "Initiator error",
52      "Authentication failure",
53      "Authorization failure",
54      "Not found",
55      "Target removed",
56      "Unsupported version",
57      "Too many connections",
58      "Missing parameter",
59      "Can't include in session",
60      "Session type not suported",
61      "Session does not exist",
62      "Invalid during login",
63 };
64 #define CLASS1_ERRS ((sizeof status_class1) / sizeof(char *))
65
66 static char *status_class3[] = {
67      "Target error",
68      "Service unavailable",
69      "Out of resources"
70 };
71 #define CLASS3_ERRS ((sizeof status_class3) / sizeof(char *))
72
73 static char *
74 selectFrom(char *str, token_t *list)
75 {
76      char       *sep, *sp;
77      token_t    *lp;
78      int        n;
79
80      sp = str;
81      do {
82           sep = strchr(sp, ',');
83           if(sep != NULL)
84                n = sep - sp;
85           else
86                n = strlen(sp);
87           
88           for(lp = list; lp->name != NULL; lp++) {
89                if(strncasecmp(lp->name, sp, n) == 0)
90                     return strdup(lp->name);
91           }
92           sp = sep + 1;
93      } while(sep != NULL);
94
95      return NULL;
96 }
97
98 static char *
99 getkeyval(char *key, pdu_t *pp)
100 {
101     char        *ptr;
102     int klen, len, n;
103
104     debug_called(3);
105
106     len = pp->ds_len;
107     ptr = (char *)pp->ds_addr;
108     klen = strlen(key);
109     while(len > klen) {
110          if(strncmp(key, ptr, klen) == 0)
111               return ptr+klen;
112          n = strlen(ptr) + 1;
113          len -= n;
114          ptr += n;
115     }
116     return 0;
117 }
118
119 static int
120 handleTgtResp(isess_t *sess, pdu_t *pp)
121 {
122      isc_opt_t  *op = sess->op;
123      char       *np, *rp, *d1, *d2;
124      int        res, l1, l2;
125      
126      res = -1;
127      if(((np = getkeyval("CHAP_N=", pp)) == NULL) ||
128         ((rp = getkeyval("CHAP_R=", pp)) == NULL))
129           goto out;
130      if(strcmp(np, op->tgtChapName? op->tgtChapName: op->initiatorName) != 0) {
131           fprintf(stderr, "%s does not match\n", np);
132           goto out;
133      }
134      l1 = str2bin(op->tgtChapDigest, &d1);
135      l2 = str2bin(rp, &d2);
136
137      debug(3, "l1=%d '%s' l2=%d '%s'", l1, op->tgtChapDigest, l2, rp);
138      if(l1 == l2 && memcmp(d1, d2, l1) == 0)
139         res = 0;
140      if(l1)
141           free(d1);
142      if(l2)
143           free(d2);
144  out:
145      free(op->tgtChapDigest);
146      op->tgtChapDigest = NULL;
147
148      debug(3, "res=%d", res);
149
150      return res;
151 }
152
153 static void
154 processParams(isess_t *sess, pdu_t *pp)
155 {
156      isc_opt_t          *op = sess->op;
157      int                len, klen, n;
158      char               *eq, *ptr;
159
160      debug_called(3);
161
162      len = pp->ds_len;
163      ptr = (char *)pp->ds_addr;
164      while(len > 0) {
165           if(vflag > 1)
166                printf("got: len=%d %s\n", len, ptr);
167           klen = 0;
168           if((eq = strchr(ptr, '=')) != NULL)
169                klen = eq - ptr;
170           if(klen > 0) {
171                if(strncmp(ptr, "TargetAddress", klen) == 0) {
172                     char        *p, *q, *ta = NULL;
173
174                     // TargetAddress=domainname[:port][,portal-group-tag]
175                     // XXX: if(op->targetAddress) free(op->targetAddress);
176                     q = op->targetAddress = strdup(eq+1);
177                     if(*q == '[') {
178                          // bracketed IPv6
179                          if((q = strchr(q, ']')) != NULL) {
180                               *q++ = '\0';
181                               ta = op->targetAddress;
182                               op->targetAddress = strdup(ta+1);
183                          } else
184                               q = op->targetAddress;
185                     }
186                     if((p = strchr(q, ',')) != NULL) {
187                          *p++ = 0;
188                          op->targetPortalGroupTag = atoi(p);
189                     }
190                     if((p = strchr(q, ':')) != NULL) {
191                          *p++ = 0;
192                          op->port = atoi(p);
193                     }
194                     if(ta)
195                          free(ta);
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           uint  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 }