1 /*
2  * Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
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 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 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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include <inttypes.h>
33 #include <stdint.h>
34 
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <pthread.h>
40 #ifdef HAVE_PTHREAD_NP_H
41 #include <pthread_np.h>
42 #endif
43 #include <unistd.h>
44 #include <sys/param.h>
45 
46 #include "istgt.h"
47 #include "istgt_ver.h"
48 #include "istgt_log.h"
49 #include "istgt_sock.h"
50 #include "istgt_misc.h"
51 #include "istgt_md5.h"
52 #include "istgt_lu.h"
53 #include "istgt_iscsi.h"
54 #include "istgt_proto.h"
55 
56 #if !defined(__GNUC__)
57 #undef __attribute__
58 #define __attribute__(x)
59 #endif
60 
61 #define TIMEOUT_RW 60
62 #define MAX_LINEBUF 4096
63 
64 typedef struct istgt_uctl_t {
65 	int id;
66 
67 	ISTGT_Ptr istgt;
68 	PORTAL portal;
69 	int sock;
70 	pthread_t thread;
71 
72 	int family;
73 	char caddr[MAX_ADDRBUF];
74 	char saddr[MAX_ADDRBUF];
75 
76 	ISTGT_CHAP_AUTH auth;
77 	int authenticated;
78 
79 	int timeout;
80 	int auth_group;
81 	int no_auth;
82 	int req_auth;
83 	int req_mutual;
84 
85 	char *mediadirectory;
86 
87 	int recvtmpsize;
88 	int recvtmpcnt;
89 	int recvtmpidx;
90 	int recvbufsize;
91 	int sendbufsize;
92 	int worksize;
93 	char recvtmp[MAX_LINEBUF];
94 	char recvbuf[MAX_LINEBUF];
95 	char sendbuf[MAX_LINEBUF];
96 	char work[MAX_LINEBUF];
97 	char *cmd;
98 	char *arg;
99 } UCTL;
100 typedef UCTL *UCTL_Ptr;
101 
102 typedef enum {
103 	UCTL_CMD_OK = 0,
104 	UCTL_CMD_ERR = 1,
105 	UCTL_CMD_EOF = 2,
106 	UCTL_CMD_QUIT = 3,
107 	UCTL_CMD_DISCON = 4,
108 } UCTL_CMD_STATUS;
109 
110 #define ARGS_DELIM " \t"
111 
112 static int
istgt_uctl_readline(UCTL_Ptr uctl)113 istgt_uctl_readline(UCTL_Ptr uctl)
114 {
115 	ssize_t total;
116 
117 	total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize,
118 	    uctl->recvtmp, uctl->recvtmpsize,
119 	    &uctl->recvtmpidx, &uctl->recvtmpcnt,
120 	    uctl->timeout);
121 	if (total < 0) {
122 		return UCTL_CMD_DISCON;
123 	}
124 	if (total == 0) {
125 		return UCTL_CMD_EOF;
126 	}
127 	return UCTL_CMD_OK;
128 }
129 
130 static int
istgt_uctl_writeline(UCTL_Ptr uctl)131 istgt_uctl_writeline(UCTL_Ptr uctl)
132 {
133 	ssize_t total;
134 	ssize_t expect;
135 
136 	expect = strlen(uctl->sendbuf);
137 	total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout);
138 	if (total < 0) {
139 		return UCTL_CMD_DISCON;
140 	}
141 	if (total != expect) {
142 		return UCTL_CMD_ERR;
143 	}
144 	return UCTL_CMD_OK;
145 }
146 
147 static int istgt_uctl_snprintf(UCTL_Ptr uctl, const char *format, ...) __attribute__((__format__(__printf__, 2, 3)));
148 
149 static int
istgt_uctl_snprintf(UCTL_Ptr uctl,const char * format,...)150 istgt_uctl_snprintf(UCTL_Ptr uctl, const char *format, ...)
151 {
152 	va_list ap;
153 	int rc;
154 
155 	va_start(ap, format);
156 	rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap);
157 	va_end(ap);
158 	return rc;
159 }
160 
161 static int
istgt_uctl_get_media_present(ISTGT_LU_Ptr lu,int lun)162 istgt_uctl_get_media_present(ISTGT_LU_Ptr lu, int lun)
163 {
164 	int rc;
165 
166 	switch (lu->type) {
167 	case ISTGT_LU_TYPE_DVD:
168 		MTX_LOCK(&lu->mutex);
169 		rc = istgt_lu_dvd_media_present(lu->lun[lun].spec);
170 		MTX_UNLOCK(&lu->mutex);
171 		break;
172 	case ISTGT_LU_TYPE_TAPE:
173 		MTX_LOCK(&lu->mutex);
174 		rc = istgt_lu_tape_media_present(lu->lun[lun].spec);
175 		MTX_UNLOCK(&lu->mutex);
176 		break;
177 	default:
178 		rc = 0;
179 	}
180 	return rc;
181 }
182 
183 static int
istgt_uctl_get_media_lock(ISTGT_LU_Ptr lu,int lun)184 istgt_uctl_get_media_lock(ISTGT_LU_Ptr lu, int lun)
185 {
186 	int rc;
187 
188 	switch (lu->type) {
189 	case ISTGT_LU_TYPE_DVD:
190 		MTX_LOCK(&lu->mutex);
191 		rc = istgt_lu_dvd_media_lock(lu->lun[lun].spec);
192 		MTX_UNLOCK(&lu->mutex);
193 		break;
194 	case ISTGT_LU_TYPE_TAPE:
195 		MTX_LOCK(&lu->mutex);
196 		rc = istgt_lu_tape_media_lock(lu->lun[lun].spec);
197 		MTX_UNLOCK(&lu->mutex);
198 		break;
199 	default:
200 		rc = 0;
201 	}
202 	return rc;
203 }
204 
205 static int
istgt_uctl_get_authinfo(UCTL_Ptr uctl,const char * authuser)206 istgt_uctl_get_authinfo(UCTL_Ptr uctl, const char *authuser)
207 {
208 	char *authfile = NULL;
209 	int ag_tag;
210 	int rc;
211 
212 	ag_tag = uctl->auth_group;
213 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ag_tag=%d\n", ag_tag);
214 
215 	MTX_LOCK(&uctl->istgt->mutex);
216 	authfile = xstrdup(uctl->istgt->authfile);
217 	MTX_UNLOCK(&uctl->istgt->mutex);
218 
219 	rc = istgt_chap_get_authinfo(&uctl->auth, authfile, authuser, ag_tag);
220 	if (rc < 0) {
221 		ISTGT_ERRLOG("chap_get_authinfo() failed\n");
222 		xfree(authfile);
223 		return -1;
224 	}
225 	xfree(authfile);
226 	return 0;
227 }
228 
229 static int
istgt_uctl_cmd_auth(UCTL_Ptr uctl)230 istgt_uctl_cmd_auth(UCTL_Ptr uctl)
231 {
232 	const char *delim = ARGS_DELIM;
233 	char *arg;
234 	char *label;
235 	char *chap_a;
236 	char *chap_i;
237 	char *chap_c;
238 	char *chap_n;
239 	char *chap_r;
240 	int rc;
241 
242 	arg = uctl->arg;
243 	label = strsepq(&arg, delim);
244 
245 	if (label == NULL) {
246 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
247 		rc = istgt_uctl_writeline(uctl);
248 		if (rc != UCTL_CMD_OK) {
249 			return rc;
250 		}
251 		return UCTL_CMD_ERR;
252 	}
253 
254 	if (strcasecmp(label, "CHAP_A") == 0) {
255 		if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_A) {
256 			istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
257 		error_return:
258 			uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A;
259 			rc = istgt_uctl_writeline(uctl);
260 			if (rc != UCTL_CMD_OK) {
261 				return rc;
262 			}
263 			return UCTL_CMD_ERR;
264 		}
265 
266 		chap_a = strsepq(&arg, delim);
267 		if (chap_a == NULL  || strcasecmp(chap_a, "5") != 0) {
268 			istgt_uctl_snprintf(uctl, "ERR invalid algorithm\n");
269 			goto error_return;
270 		}
271 
272 		/* Identifier is one octet */
273 		istgt_gen_random(uctl->auth.chap_id, 1);
274 		/* Challenge Value is a variable stream of octets */
275 		/* (binary length MUST not exceed 1024 bytes) */
276 		uctl->auth.chap_challenge_len = ISTGT_CHAP_CHALLENGE_LEN;
277 		istgt_gen_random(uctl->auth.chap_challenge,
278 		    uctl->auth.chap_challenge_len);
279 
280 		istgt_bin2hex(uctl->work, uctl->worksize,
281 		    uctl->auth.chap_challenge,
282 		    uctl->auth.chap_challenge_len);
283 
284 		istgt_uctl_snprintf(uctl, "%s CHAP_IC %d %s\n",
285 		    uctl->cmd, (int) uctl->auth.chap_id[0],
286 		    uctl->work);
287 
288 		rc = istgt_uctl_writeline(uctl);
289 		if (rc != UCTL_CMD_OK) {
290 			return rc;
291 		}
292 		uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_NR;
293 		/* 3-way handshake */
294 		return UCTL_CMD_OK;
295 	} else if (strcasecmp(label, "CHAP_NR") == 0) {
296 		uint8_t resmd5[ISTGT_MD5DIGEST_LEN];
297 		uint8_t tgtmd5[ISTGT_MD5DIGEST_LEN];
298 		ISTGT_MD5CTX md5ctx;
299 
300 		if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_NR) {
301 			istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
302 			goto error_return;
303 		}
304 
305 		chap_n = strsepq(&arg, delim);
306 		chap_r = strsepq(&arg, delim);
307 		if (chap_n == NULL || chap_r == NULL) {
308 			istgt_uctl_snprintf(uctl, "ERR no response\n");
309 			goto error_return;
310 		}
311 		//ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "N=%s, R=%s\n", chap_n, chap_r);
312 
313 		rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r);
314 		if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) {
315 			istgt_uctl_snprintf(uctl, "ERR response format error\n");
316 			goto error_return;
317 		}
318 
319 		rc = istgt_uctl_get_authinfo(uctl, chap_n);
320 		if (rc < 0) {
321 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
322 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
323 			goto error_return;
324 		}
325 		if (uctl->auth.user == NULL || uctl->auth.secret == NULL) {
326 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
327 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
328 			goto error_return;
329 		}
330 
331 		istgt_md5init(&md5ctx);
332 		/* Identifier */
333 		istgt_md5update(&md5ctx, uctl->auth.chap_id, 1);
334 		/* followed by secret */
335 		istgt_md5update(&md5ctx, uctl->auth.secret,
336 		    strlen(uctl->auth.secret));
337 		/* followed by Challenge Value */
338 		istgt_md5update(&md5ctx, uctl->auth.chap_challenge,
339 		    uctl->auth.chap_challenge_len);
340 		/* tgtmd5 is expecting Response Value */
341 		istgt_md5final(tgtmd5, &md5ctx);
342 
343 		/* compare MD5 digest */
344 		if (memcmp(tgtmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) {
345 			/* not match */
346 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
347 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
348 			goto error_return;
349 		}
350 		/* OK client's secret */
351 		uctl->authenticated = 1;
352 
353 		/* mutual CHAP? */
354 		chap_i = strsepq(&arg, delim);
355 		chap_c = strsepq(&arg, delim);
356 		if (chap_i != NULL && chap_c != NULL) {
357 			/* Identifier */
358 			uctl->auth.chap_mid[0] = (uint8_t) strtol(chap_i, NULL, 10);
359 			/* Challenge Value */
360 			rc = istgt_hex2bin(uctl->auth.chap_mchallenge,
361 			    ISTGT_CHAP_CHALLENGE_LEN, chap_c);
362 			if (rc < 0) {
363 				istgt_uctl_snprintf(uctl, "ERR challenge format error\n");
364 				goto error_return;
365 			}
366 			uctl->auth.chap_mchallenge_len = rc;
367 
368 			if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) {
369 				ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
370 				istgt_uctl_snprintf(uctl,
371 				    "ERR auth user or secret is missing\n");
372 				goto error_return;
373 			}
374 
375 			istgt_md5init(&md5ctx);
376 			/* Identifier */
377 			istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1);
378 			/* followed by secret */
379 			istgt_md5update(&md5ctx, uctl->auth.msecret,
380 			    strlen(uctl->auth.msecret));
381 			/* followed by Challenge Value */
382 			istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge,
383 			    uctl->auth.chap_mchallenge_len);
384 			/* tgtmd5 is Response Value */
385 			istgt_md5final(tgtmd5, &md5ctx);
386 
387 			istgt_bin2hex(uctl->work, uctl->worksize,
388 			    tgtmd5, ISTGT_MD5DIGEST_LEN);
389 
390 			/* send NR for mutual CHAP */
391 			istgt_uctl_snprintf(uctl, "%s CHAP_NR \"%s\" %s\n",
392 			    uctl->cmd,
393 			    uctl->auth.muser,
394 			    uctl->work);
395 			rc = istgt_uctl_writeline(uctl);
396 			if (rc != UCTL_CMD_OK) {
397 				return rc;
398 			}
399 		} else {
400 			/* not mutual */
401 			if (uctl->req_mutual) {
402 				ISTGT_ERRLOG("required mutual CHAP\n");
403 				istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
404 				goto error_return;
405 			}
406 		}
407 
408 		uctl->auth.chap_phase = ISTGT_CHAP_PHASE_END;
409 	} else {
410 		istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
411 		goto error_return;
412 	}
413 
414 	/* auth succeeded (but mutual may fail) */
415 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
416 	rc = istgt_uctl_writeline(uctl);
417 	if (rc != UCTL_CMD_OK) {
418 		return rc;
419 	}
420 	return UCTL_CMD_OK;
421 }
422 
423 static int
istgt_uctl_cmd_quit(UCTL_Ptr uctl)424 istgt_uctl_cmd_quit(UCTL_Ptr uctl)
425 {
426 	int rc;
427 
428 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
429 	rc = istgt_uctl_writeline(uctl);
430 	if (rc != UCTL_CMD_OK) {
431 		return rc;
432 	}
433 	return UCTL_CMD_QUIT;
434 }
435 
436 static int
istgt_uctl_cmd_noop(UCTL_Ptr uctl)437 istgt_uctl_cmd_noop(UCTL_Ptr uctl)
438 {
439 	int rc;
440 
441 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
442 	rc = istgt_uctl_writeline(uctl);
443 	if (rc != UCTL_CMD_OK) {
444 		return rc;
445 	}
446 	return UCTL_CMD_OK;
447 }
448 
449 static int
istgt_uctl_cmd_version(UCTL_Ptr uctl)450 istgt_uctl_cmd_version(UCTL_Ptr uctl)
451 {
452 	int rc;
453 
454 	istgt_uctl_snprintf(uctl, "%s %s (%s)\n", uctl->cmd,
455 	    ISTGT_VERSION, ISTGT_EXTRA_VERSION);
456 	rc = istgt_uctl_writeline(uctl);
457 	if (rc != UCTL_CMD_OK) {
458 		return rc;
459 	}
460 
461 	/* version succeeded */
462 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
463 	rc = istgt_uctl_writeline(uctl);
464 	if (rc != UCTL_CMD_OK) {
465 		return rc;
466 	}
467 	return UCTL_CMD_OK;
468 }
469 
470 static int
istgt_uctl_cmd_list(UCTL_Ptr uctl)471 istgt_uctl_cmd_list(UCTL_Ptr uctl)
472 {
473 	ISTGT_LU_Ptr lu;
474 	ISTGT_LU_LUN_Ptr llp;
475 	const char *delim = ARGS_DELIM;
476 	char *arg;
477 	char *iqn;
478 	char *lun;
479 	char *mflags;
480 	char *mfile;
481 	char *msize;
482 	char *mtype;
483 	char *workp;
484 	int lun_i;
485 	int worksize;
486 	int present;
487 	int lock;
488 	int rc;
489 	int i;
490 
491 	arg = uctl->arg;
492 	iqn = strsepq(&arg, delim);
493 	lun = strsepq(&arg, delim);
494 
495 	if (arg != NULL) {
496 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
497 		rc = istgt_uctl_writeline(uctl);
498 		if (rc != UCTL_CMD_OK) {
499 			return rc;
500 		}
501 		return UCTL_CMD_ERR;
502 	}
503 
504 	if (iqn == NULL) {
505 		/* all targets */
506 		MTX_LOCK(&uctl->istgt->mutex);
507 		for (i = 0; i < MAX_LOGICAL_UNIT; i++) {
508 			lu = uctl->istgt->logical_unit[i];
509 			if (lu == NULL)
510 				continue;
511 			istgt_uctl_snprintf(uctl, "%s %s\n", uctl->cmd, lu->name);
512 			rc = istgt_uctl_writeline(uctl);
513 			if (rc != UCTL_CMD_OK) {
514 				MTX_UNLOCK(&uctl->istgt->mutex);
515 				return rc;
516 			}
517 		}
518 		MTX_UNLOCK(&uctl->istgt->mutex);
519 	} else {
520 		/* specified target */
521 		MTX_LOCK(&uctl->istgt->mutex);
522 		if (lun == NULL) {
523 			lun_i = 0;
524 		} else {
525 			lun_i = (int) strtol(lun, NULL, 10);
526 		}
527 		lu = istgt_lu_find_target(uctl->istgt, iqn);
528 		if (lu == NULL) {
529 			MTX_UNLOCK(&uctl->istgt->mutex);
530 			istgt_uctl_snprintf(uctl, "ERR no target\n");
531 		error_return:
532 			rc = istgt_uctl_writeline(uctl);
533 			if (rc != UCTL_CMD_OK) {
534 				return rc;
535 			}
536 			return UCTL_CMD_ERR;
537 		}
538 		if (lun_i < 0 || lun_i >= lu->maxlun) {
539 			MTX_UNLOCK(&uctl->istgt->mutex);
540 			istgt_uctl_snprintf(uctl, "ERR no target\n");
541 			goto error_return;
542 		}
543 		llp = &lu->lun[lun_i];
544 
545 		worksize = uctl->worksize;
546 		workp = uctl->work;
547 
548 		switch (llp->type) {
549 		case ISTGT_LU_LUN_TYPE_REMOVABLE:
550 			mflags = istgt_lu_get_media_flags_string(llp->u.removable.flags,
551 			    workp, worksize);
552 			worksize -= strlen(mflags) + 1;
553 			workp += strlen(mflags) + 1;
554 			present = istgt_uctl_get_media_present(lu, lun_i);
555 			lock = istgt_uctl_get_media_lock(lu, lun_i);
556 			mfile = llp->u.removable.file;
557 			if (llp->u.removable.flags & ISTGT_LU_FLAG_MEDIA_AUTOSIZE) {
558 				snprintf(workp, worksize, "auto");
559 			} else {
560 				snprintf(workp, worksize, "%"PRIu64,
561 				    llp->u.removable.size);
562 			}
563 			msize = workp;
564 			worksize -= strlen(msize) + 1;
565 			workp += strlen(msize) + 1;
566 			snprintf(workp, worksize, "-");
567 			mtype = workp;
568 			worksize -= strlen(msize) + 1;
569 			workp += strlen(msize) + 1;
570 
571 			istgt_uctl_snprintf(uctl, "%s lun%u %s %s %s %s %s \"%s\" %s\n",
572 			    uctl->cmd, lun_i,
573 			    "removable",
574 			    (present ? "present" : "absent"),
575 			    (lock ? "lock" : "unlock"),
576 			    mtype, mflags, mfile, msize);
577 			rc = istgt_uctl_writeline(uctl);
578 			break;
579 		case ISTGT_LU_LUN_TYPE_STORAGE:
580 			mfile = llp->u.storage.file;
581 			snprintf(workp, worksize, "%"PRIu64,
582 			    llp->u.storage.size);
583 			msize = workp;
584 			worksize -= strlen(msize) + 1;
585 			workp += strlen(msize) + 1;
586 
587 			istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\" %s\n",
588 			    uctl->cmd, lun_i,
589 			    "storage",
590 			    mfile, msize);
591 			rc = istgt_uctl_writeline(uctl);
592 			break;
593 		case ISTGT_LU_LUN_TYPE_DEVICE:
594 			mfile = llp->u.device.file;
595 
596 			istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\"\n",
597 			    uctl->cmd, lun_i,
598 			    "device",
599 			    mfile);
600 			rc = istgt_uctl_writeline(uctl);
601 			break;
602 		case ISTGT_LU_LUN_TYPE_SLOT:
603 		default:
604 			MTX_UNLOCK(&uctl->istgt->mutex);
605 			istgt_uctl_snprintf(uctl, "ERR unsupport LUN type\n");
606 			goto error_return;
607 		}
608 
609 		if (rc != UCTL_CMD_OK) {
610 			MTX_UNLOCK(&uctl->istgt->mutex);
611 			return rc;
612 		}
613 		MTX_UNLOCK(&uctl->istgt->mutex);
614 	}
615 
616 	/* list succeeded */
617 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
618 	rc = istgt_uctl_writeline(uctl);
619 	if (rc != UCTL_CMD_OK) {
620 		return rc;
621 	}
622 	return UCTL_CMD_OK;
623 }
624 
625 static int
istgt_uctl_cmd_unload(UCTL_Ptr uctl)626 istgt_uctl_cmd_unload(UCTL_Ptr uctl)
627 {
628 	ISTGT_LU_Ptr lu;
629 	ISTGT_LU_LUN_Ptr llp;
630 	const char *delim = ARGS_DELIM;
631 	char *arg;
632 	char *iqn;
633 	char *lun;
634 	int lun_i;
635 	int rc;
636 
637 	arg = uctl->arg;
638 	iqn = strsepq(&arg, delim);
639 	lun = strsepq(&arg, delim);
640 
641 	if (iqn == NULL || arg != NULL) {
642 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
643 		rc = istgt_uctl_writeline(uctl);
644 		if (rc != UCTL_CMD_OK) {
645 			return rc;
646 		}
647 		return UCTL_CMD_ERR;
648 	}
649 
650 	if (lun == NULL) {
651 		lun_i = 0;
652 	} else {
653 		lun_i = (int) strtol(lun, NULL, 10);
654 	}
655 	lu = istgt_lu_find_target(uctl->istgt, iqn);
656 	if (lu == NULL) {
657 		istgt_uctl_snprintf(uctl, "ERR no target\n");
658 	error_return:
659 		rc = istgt_uctl_writeline(uctl);
660 		if (rc != UCTL_CMD_OK) {
661 			return rc;
662 		}
663 		return UCTL_CMD_ERR;
664 	}
665 	if (lun_i < 0 || lun_i >= lu->maxlun) {
666 		istgt_uctl_snprintf(uctl, "ERR no target\n");
667 		goto error_return;
668 	}
669 	llp = &lu->lun[lun_i];
670 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
671 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
672 		goto error_return;
673 	}
674 
675 	/* unload media from lun */
676 	switch (lu->type) {
677 	case ISTGT_LU_TYPE_DVD:
678 		MTX_LOCK(&lu->mutex);
679 		rc = istgt_lu_dvd_unload_media(lu->lun[lun_i].spec);
680 		MTX_UNLOCK(&lu->mutex);
681 		break;
682 	case ISTGT_LU_TYPE_TAPE:
683 		MTX_LOCK(&lu->mutex);
684 		rc = istgt_lu_tape_unload_media(lu->lun[lun_i].spec);
685 		MTX_UNLOCK(&lu->mutex);
686 		break;
687 	default:
688 		rc = -1;
689 	}
690 
691 	if (rc < 0) {
692 		istgt_uctl_snprintf(uctl, "ERR unload\n");
693 		rc = istgt_uctl_writeline(uctl);
694 		if (rc != UCTL_CMD_OK) {
695 			return rc;
696 		}
697 		return UCTL_CMD_ERR;
698 	}
699 
700 	/* logging event */
701 	ISTGT_NOTICELOG("Media Unload %s lun%d from %s\n",
702 	    iqn, lun_i, uctl->caddr);
703 
704 	/* unload succeeded */
705 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
706 	rc = istgt_uctl_writeline(uctl);
707 	if (rc != UCTL_CMD_OK) {
708 		return rc;
709 	}
710 	return UCTL_CMD_OK;
711 }
712 
713 static int
istgt_uctl_cmd_load(UCTL_Ptr uctl)714 istgt_uctl_cmd_load(UCTL_Ptr uctl)
715 {
716 	ISTGT_LU_Ptr lu;
717 	ISTGT_LU_LUN_Ptr llp;
718 	const char *delim = ARGS_DELIM;
719 	char *arg;
720 	char *iqn;
721 	char *lun;
722 	int lun_i;
723 	int rc;
724 
725 	arg = uctl->arg;
726 	iqn = strsepq(&arg, delim);
727 	lun = strsepq(&arg, delim);
728 
729 	if (iqn == NULL || arg != NULL) {
730 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
731 		rc = istgt_uctl_writeline(uctl);
732 		if (rc != UCTL_CMD_OK) {
733 			return rc;
734 		}
735 		return UCTL_CMD_ERR;
736 	}
737 
738 	if (lun == NULL) {
739 		lun_i = 0;
740 	} else {
741 		lun_i = (int) strtol(lun, NULL, 10);
742 	}
743 	lu = istgt_lu_find_target(uctl->istgt, iqn);
744 	if (lu == NULL) {
745 		istgt_uctl_snprintf(uctl, "ERR no target\n");
746 	error_return:
747 		rc = istgt_uctl_writeline(uctl);
748 		if (rc != UCTL_CMD_OK) {
749 			return rc;
750 		}
751 		return UCTL_CMD_ERR;
752 	}
753 	if (lun_i < 0 || lun_i >= lu->maxlun) {
754 		istgt_uctl_snprintf(uctl, "ERR no target\n");
755 		goto error_return;
756 	}
757 	llp = &lu->lun[lun_i];
758 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
759 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
760 		goto error_return;
761 	}
762 
763 	/* load media to lun */
764 	switch (lu->type) {
765 	case ISTGT_LU_TYPE_DVD:
766 		MTX_LOCK(&lu->mutex);
767 		rc = istgt_lu_dvd_load_media(lu->lun[lun_i].spec);
768 		MTX_UNLOCK(&lu->mutex);
769 		break;
770 	case ISTGT_LU_TYPE_TAPE:
771 		MTX_LOCK(&lu->mutex);
772 		rc = istgt_lu_tape_load_media(lu->lun[lun_i].spec);
773 		MTX_UNLOCK(&lu->mutex);
774 		break;
775 	default:
776 		rc = -1;
777 	}
778 
779 	if (rc < 0) {
780 		istgt_uctl_snprintf(uctl, "ERR load\n");
781 		rc = istgt_uctl_writeline(uctl);
782 		if (rc != UCTL_CMD_OK) {
783 			return rc;
784 		}
785 		return UCTL_CMD_ERR;
786 	}
787 
788 	/* logging event */
789 	ISTGT_NOTICELOG("Media Load %s lun%d from %s\n",
790 	    iqn, lun_i, uctl->caddr);
791 
792 	/* load succeeded */
793 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
794 	rc = istgt_uctl_writeline(uctl);
795 	if (rc != UCTL_CMD_OK) {
796 		return rc;
797 	}
798 	return UCTL_CMD_OK;
799 }
800 
801 static int
istgt_uctl_cmd_change(UCTL_Ptr uctl)802 istgt_uctl_cmd_change(UCTL_Ptr uctl)
803 {
804 	ISTGT_LU_Ptr lu;
805 	ISTGT_LU_LUN_Ptr llp;
806 	const char *delim = ARGS_DELIM;
807 	char empty_flags[] = "ro";
808 	char empty_size[] = "0";
809 	char *arg;
810 	char *iqn;
811 	char *lun;
812 	char *type;
813 	char *flags;
814 	char *file;
815 	char *size;
816 	char *safedir;
817 	char *fullpath;
818 	char *abspath;
819 	int lun_i;
820 	int len;
821 	int rc;
822 
823 	arg = uctl->arg;
824 	iqn = strsepq(&arg, delim);
825 	lun = strsepq(&arg, delim);
826 
827 	type = strsepq(&arg, delim);
828 	flags = strsepq(&arg, delim);
829 	file = strsepq(&arg, delim);
830 	size = strsepq(&arg, delim);
831 
832 	if (iqn == NULL || lun == NULL || type == NULL || flags == NULL
833 	    || file == NULL || size == NULL || arg != NULL) {
834 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
835 		rc = istgt_uctl_writeline(uctl);
836 		if (rc != UCTL_CMD_OK) {
837 			return rc;
838 		}
839 		return UCTL_CMD_ERR;
840 	}
841 
842 	if (lun == NULL) {
843 		lun_i = 0;
844 	} else {
845 		lun_i = (int) strtol(lun, NULL, 10);
846 	}
847 	lu = istgt_lu_find_target(uctl->istgt, iqn);
848 	if (lu == NULL) {
849 		istgt_uctl_snprintf(uctl, "ERR no target\n");
850 	error_return:
851 		rc = istgt_uctl_writeline(uctl);
852 		if (rc != UCTL_CMD_OK) {
853 			return rc;
854 		}
855 		return UCTL_CMD_ERR;
856 	}
857 	if (lun_i < 0 || lun_i >= lu->maxlun) {
858 		istgt_uctl_snprintf(uctl, "ERR no target\n");
859 		goto error_return;
860 	}
861 	llp = &lu->lun[lun_i];
862 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
863 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
864 		goto error_return;
865 	}
866 
867 	/* make safe directory (start '/', end '/') */
868 	len = 1 + strlen(uctl->mediadirectory) + 1 + 1;
869 	safedir = xmalloc(len);
870 	if (uctl->mediadirectory[0] != '/') {
871 		ISTGT_WARNLOG("MediaDirectory is not starting with '/'\n");
872 		snprintf(safedir, len, "/%s", uctl->mediadirectory);
873 	} else {
874 		snprintf(safedir, len, "%s", uctl->mediadirectory);
875 	}
876 	if (strlen(safedir) > 1 && safedir[strlen(safedir) - 1] != '/') {
877 		safedir[strlen(safedir) + 1] = '\0';
878 		safedir[strlen(safedir)] = '/';
879 	}
880 
881 	/* check abspath in mediadirectory? */
882 	len = strlen(safedir) + strlen(file) + 1;
883 	fullpath = xmalloc(len);
884 	if (file[0] != '/') {
885 		snprintf(fullpath, len, "%s%s", safedir, file);
886 	} else {
887 		snprintf(fullpath, len, "%s", file);
888 	}
889 #ifdef PATH_MAX
890 	abspath = xmalloc(len + PATH_MAX);
891 	file = realpath(fullpath, abspath);
892 #else
893 /*
894 	{
895 		long path_max;
896 		path_max = pathconf(fullpath, _PC_PATH_MAX);
897 		if (path_max != -1L) {
898 			abspath = xmalloc(path_max);
899 			file = realpath(fullpath, abspath);
900 		}
901 	}
902 */
903 	file = abspath = realpath(fullpath, NULL);
904 #endif /* PATH_MAX */
905 	if (file == NULL) {
906 		ISTGT_ERRLOG("realpath(%s) failed\n", fullpath);
907 	internal_error:
908 		xfree(safedir);
909 		xfree(fullpath);
910 		xfree(abspath);
911 		istgt_uctl_snprintf(uctl, "ERR %s internal error\n", uctl->cmd);
912 		rc = istgt_uctl_writeline(uctl);
913 		if (rc != UCTL_CMD_OK) {
914 			return rc;
915 		}
916 		return UCTL_CMD_ERR;
917 	}
918 	if (strcasecmp(file, "/dev/null") == 0) {
919 		/* OK, empty slot */
920 		flags = empty_flags;
921 		size = empty_size;
922 	} else if (strncasecmp(file, safedir, strlen(safedir)) != 0) {
923 		ISTGT_ERRLOG("Realpath(%s) is not within MediaDirectory(%s)\n",
924 		    file, safedir);
925 		goto internal_error;
926 	}
927 
928 	/* unload and load media from lun */
929 	switch (lu->type) {
930 	case ISTGT_LU_TYPE_DVD:
931 		MTX_LOCK(&lu->mutex);
932 		rc = istgt_lu_dvd_change_media(lu->lun[lun_i].spec,
933 		    type, flags, file, size);
934 		MTX_UNLOCK(&lu->mutex);
935 		break;
936 	case ISTGT_LU_TYPE_TAPE:
937 		MTX_LOCK(&lu->mutex);
938 		rc = istgt_lu_tape_change_media(lu->lun[lun_i].spec,
939 		    type, flags, file, size);
940 		MTX_UNLOCK(&lu->mutex);
941 		break;
942 	default:
943 		rc = -1;
944 	}
945 
946 	if (rc < 0) {
947 		xfree(safedir);
948 		xfree(fullpath);
949 		xfree(abspath);
950 		istgt_uctl_snprintf(uctl, "ERR change\n");
951 		rc = istgt_uctl_writeline(uctl);
952 		if (rc != UCTL_CMD_OK) {
953 			return rc;
954 		}
955 		return UCTL_CMD_ERR;
956 	}
957 
958 	/* logging event */
959 	ISTGT_NOTICELOG("Media Change \"%s %s %s %s\" on %s lun%d from %s\n",
960 	    type, flags, file, size, iqn, lun_i, uctl->caddr);
961 
962 	xfree(safedir);
963 	xfree(fullpath);
964 	xfree(abspath);
965 
966 	/* change succeeded */
967 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
968 	rc = istgt_uctl_writeline(uctl);
969 	if (rc != UCTL_CMD_OK) {
970 		return rc;
971 	}
972 	return UCTL_CMD_OK;
973 }
974 
975 static int
istgt_uctl_cmd_reset(UCTL_Ptr uctl)976 istgt_uctl_cmd_reset(UCTL_Ptr uctl)
977 {
978 	ISTGT_LU_Ptr lu;
979 	ISTGT_LU_LUN_Ptr llp;
980 	const char *delim = ARGS_DELIM;
981 	char *arg;
982 	char *iqn;
983 	char *lun;
984 	int lun_i;
985 	int rc;
986 
987 	arg = uctl->arg;
988 	iqn = strsepq(&arg, delim);
989 	lun = strsepq(&arg, delim);
990 
991 	if (iqn == NULL || arg != NULL) {
992 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
993 		rc = istgt_uctl_writeline(uctl);
994 		if (rc != UCTL_CMD_OK) {
995 			return rc;
996 		}
997 		return UCTL_CMD_ERR;
998 	}
999 
1000 	if (lun == NULL) {
1001 		lun_i = 0;
1002 	} else {
1003 		lun_i = (int) strtol(lun, NULL, 10);
1004 	}
1005 	lu = istgt_lu_find_target(uctl->istgt, iqn);
1006 	if (lu == NULL) {
1007 		istgt_uctl_snprintf(uctl, "ERR no target\n");
1008 	error_return:
1009 		rc = istgt_uctl_writeline(uctl);
1010 		if (rc != UCTL_CMD_OK) {
1011 			return rc;
1012 		}
1013 		return UCTL_CMD_ERR;
1014 	}
1015 	if (lun_i < 0 || lun_i >= lu->maxlun) {
1016 		istgt_uctl_snprintf(uctl, "ERR no target\n");
1017 		goto error_return;
1018 	}
1019 	llp = &lu->lun[lun_i];
1020 	if (llp->type == ISTGT_LU_LUN_TYPE_NONE) {
1021 		istgt_uctl_snprintf(uctl, "ERR no LUN\n");
1022 		goto error_return;
1023 	}
1024 
1025 	/* reset lun */
1026 	switch (lu->type) {
1027 	case ISTGT_LU_TYPE_DISK:
1028 		MTX_LOCK(&lu->mutex);
1029 		rc = istgt_lu_disk_reset(lu, lun_i);
1030 		MTX_UNLOCK(&lu->mutex);
1031 		break;
1032 	case ISTGT_LU_TYPE_DVD:
1033 		MTX_LOCK(&lu->mutex);
1034 		rc = istgt_lu_dvd_reset(lu, lun_i);
1035 		MTX_UNLOCK(&lu->mutex);
1036 		break;
1037 	case ISTGT_LU_TYPE_TAPE:
1038 		MTX_LOCK(&lu->mutex);
1039 		rc = istgt_lu_tape_reset(lu, lun_i);
1040 		MTX_UNLOCK(&lu->mutex);
1041 		break;
1042 	case ISTGT_LU_TYPE_NONE:
1043 	case ISTGT_LU_TYPE_PASS:
1044 		rc = -1;
1045 		break;
1046 	default:
1047 		rc = -1;
1048 	}
1049 
1050 	if (rc < 0) {
1051 		istgt_uctl_snprintf(uctl, "ERR reset\n");
1052 		rc = istgt_uctl_writeline(uctl);
1053 		if (rc != UCTL_CMD_OK) {
1054 			return rc;
1055 		}
1056 		return UCTL_CMD_ERR;
1057 	}
1058 
1059 	/* logging event */
1060 	ISTGT_NOTICELOG("Unit Reset %s lun%d from %s\n",
1061 	    iqn, lun_i, uctl->caddr);
1062 
1063 	/* reset succeeded */
1064 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
1065 	rc = istgt_uctl_writeline(uctl);
1066 	if (rc != UCTL_CMD_OK) {
1067 		return rc;
1068 	}
1069 	return UCTL_CMD_OK;
1070 }
1071 
1072 static int
istgt_uctl_cmd_info(UCTL_Ptr uctl)1073 istgt_uctl_cmd_info(UCTL_Ptr uctl)
1074 {
1075 	ISTGT_LU_Ptr lu;
1076 	CONN_Ptr conn;
1077 	SESS_Ptr sess;
1078 	const char *delim = ARGS_DELIM;
1079 	char *arg;
1080 	char *iqn;
1081 	int ncount;
1082 	int rc;
1083 	int i, j, k;
1084 
1085 	arg = uctl->arg;
1086 	iqn = strsepq(&arg, delim);
1087 
1088 	if (arg != NULL) {
1089 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
1090 		rc = istgt_uctl_writeline(uctl);
1091 		if (rc != UCTL_CMD_OK) {
1092 			return rc;
1093 		}
1094 		return UCTL_CMD_ERR;
1095 	}
1096 
1097 	ncount = 0;
1098 	MTX_LOCK(&uctl->istgt->mutex);
1099 	for (i = 0; i < MAX_LOGICAL_UNIT; i++) {
1100 		lu = uctl->istgt->logical_unit[i];
1101 		if (lu == NULL)
1102 			continue;
1103 		if (iqn != NULL && strcasecmp(iqn, lu->name) != 0)
1104 			continue;
1105 
1106 		istgt_lock_gconns();
1107 		MTX_LOCK(&lu->mutex);
1108 		for (j = 1; j < MAX_LU_TSIH; j++) {
1109 			if (lu->tsih[j].initiator_port != NULL
1110 				&& lu->tsih[j].tsih != 0) {
1111 				conn = istgt_find_conn(lu->tsih[j].initiator_port,
1112 				    lu->name, lu->tsih[j].tsih);
1113 				if (conn == NULL || conn->sess == NULL)
1114 					continue;
1115 
1116 				sess = conn->sess;
1117 				MTX_LOCK(&sess->mutex);
1118 				for (k = 0; k < sess->connections; k++) {
1119 					conn = sess->conns[k];
1120 					if (conn == NULL)
1121 						continue;
1122 
1123 					istgt_uctl_snprintf(uctl, "%s Login from %s (%s) on %s LU%d"
1124 					    " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
1125 					    " CID=%u, HeaderDigest=%s, DataDigest=%s,"
1126 					    " MaxConnections=%u,"
1127 					    " FirstBurstLength=%u, MaxBurstLength=%u,"
1128 					    " MaxRecvDataSegmentLength=%u,"
1129 					    " InitialR2T=%s, ImmediateData=%s\n",
1130 					    uctl->cmd,
1131 					    conn->initiator_name,
1132 					    conn->initiator_addr,
1133 					    conn->target_name, lu->num,
1134 					    conn->portal.host, conn->portal.port,
1135 					    conn->portal.tag,
1136 					    conn->sess->isid, conn->sess->tsih,
1137 					    conn->cid,
1138 					    (conn->header_digest ? "on" : "off"),
1139 					    (conn->data_digest ? "on" : "off"),
1140 					    conn->sess->MaxConnections,
1141 					    conn->sess->FirstBurstLength,
1142 					    conn->sess->MaxBurstLength,
1143 					    conn->MaxRecvDataSegmentLength,
1144 					    (conn->sess->initial_r2t ? "Yes" : "No"),
1145 					    (conn->sess->immediate_data ? "Yes" : "No"));
1146 					rc = istgt_uctl_writeline(uctl);
1147 					if (rc != UCTL_CMD_OK) {
1148 						MTX_UNLOCK(&sess->mutex);
1149 						MTX_UNLOCK(&lu->mutex);
1150 						istgt_unlock_gconns();
1151 						MTX_UNLOCK(&uctl->istgt->mutex);
1152 						return rc;
1153 					}
1154 					ncount++;
1155 				}
1156 				MTX_UNLOCK(&sess->mutex);
1157 			}
1158 		}
1159 		MTX_UNLOCK(&lu->mutex);
1160 		istgt_unlock_gconns();
1161 	}
1162 	MTX_UNLOCK(&uctl->istgt->mutex);
1163 	if (ncount == 0) {
1164 		istgt_uctl_snprintf(uctl, "%s no login\n", uctl->cmd);
1165 		rc = istgt_uctl_writeline(uctl);
1166 		if (rc != UCTL_CMD_OK) {
1167 			return rc;
1168 		}
1169 	}
1170 
1171 	/* info succeeded */
1172 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
1173 	rc = istgt_uctl_writeline(uctl);
1174 	if (rc != UCTL_CMD_OK) {
1175 		return rc;
1176 	}
1177 	return UCTL_CMD_OK;
1178 }
1179 
1180 
1181 typedef struct istgt_uctl_cmd_table_t
1182 {
1183 	const char *name;
1184 	int (*func) (UCTL_Ptr uctl);
1185 } ISTGT_UCTL_CMD_TABLE;
1186 
1187 static ISTGT_UCTL_CMD_TABLE istgt_uctl_cmd_table[] =
1188 {
1189 	{ "AUTH",    istgt_uctl_cmd_auth },
1190 	{ "QUIT",    istgt_uctl_cmd_quit },
1191 	{ "NOOP",    istgt_uctl_cmd_noop },
1192 	{ "VERSION", istgt_uctl_cmd_version },
1193 	{ "LIST",    istgt_uctl_cmd_list },
1194 	{ "UNLOAD",  istgt_uctl_cmd_unload },
1195 	{ "LOAD",    istgt_uctl_cmd_load },
1196 	{ "CHANGE",  istgt_uctl_cmd_change },
1197 	{ "RESET",   istgt_uctl_cmd_reset },
1198 	{ "INFO",    istgt_uctl_cmd_info },
1199 	{ NULL,      NULL },
1200 };
1201 
1202 static int
istgt_uctl_cmd_execute(UCTL_Ptr uctl)1203 istgt_uctl_cmd_execute(UCTL_Ptr uctl)
1204 {
1205 	int (*func) (UCTL_Ptr);
1206 	const char *delim = ARGS_DELIM;
1207 	char *arg;
1208 	char *cmd;
1209 	int rc;
1210 	int i;
1211 
1212 	arg = trim_string(uctl->recvbuf);
1213 	cmd = strsepq(&arg, delim);
1214 	uctl->arg = arg;
1215 	uctl->cmd = strupr(cmd);
1216 
1217 	func = NULL;
1218 	for (i = 0; istgt_uctl_cmd_table[i].name != NULL; i++) {
1219 		if (cmd[0] == istgt_uctl_cmd_table[i].name[0]
1220 		    && strcmp(cmd, istgt_uctl_cmd_table[i].name) == 0) {
1221 			func = istgt_uctl_cmd_table[i].func;
1222 			break;
1223 		}
1224 	}
1225 	if (func == NULL) {
1226 		istgt_uctl_snprintf(uctl, "ERR unknown command\n");
1227 		rc = istgt_uctl_writeline(uctl);
1228 		if (rc != UCTL_CMD_OK) {
1229 			return UCTL_CMD_DISCON;
1230 		}
1231 		return UCTL_CMD_ERR;
1232 	}
1233 
1234 	if (uctl->no_auth
1235 	    && (strcasecmp(cmd, "AUTH") == 0)) {
1236 		istgt_uctl_snprintf(uctl, "ERR auth not required\n");
1237 		rc = istgt_uctl_writeline(uctl);
1238 		if (rc != UCTL_CMD_OK) {
1239 			return UCTL_CMD_DISCON;
1240 		}
1241 		return UCTL_CMD_ERR;
1242 	}
1243 	if (uctl->req_auth && uctl->authenticated == 0
1244 	    && !(strcasecmp(cmd, "QUIT") == 0
1245 		|| strcasecmp(cmd, "AUTH") == 0)) {
1246 		istgt_uctl_snprintf(uctl, "ERR auth required\n");
1247 		rc = istgt_uctl_writeline(uctl);
1248 		if (rc != UCTL_CMD_OK) {
1249 			return UCTL_CMD_DISCON;
1250 		}
1251 		return UCTL_CMD_ERR;
1252 	}
1253 
1254 	rc = func(uctl);
1255 	return rc;
1256 }
1257 
1258 static void istgt_free_uctl(UCTL_Ptr uctl);
1259 
1260 static void *
uctlworker(void * arg)1261 uctlworker(void *arg)
1262 {
1263 	UCTL_Ptr uctl = (UCTL_Ptr) arg;
1264 	int rc;
1265 
1266 	ISTGT_TRACELOG(ISTGT_TRACE_NET, "connect to %s:%s,%d\n",
1267 	    uctl->portal.host, uctl->portal.port, uctl->portal.tag);
1268 
1269 	istgt_uctl_snprintf(uctl, "iSCSI Target Controller version %s (%s)"
1270 	    " on %s from %s\n",
1271 	    ISTGT_VERSION, ISTGT_EXTRA_VERSION,
1272 	    uctl->saddr, uctl->caddr);
1273 	rc = istgt_uctl_writeline(uctl);
1274 	if (rc != UCTL_CMD_OK) {
1275 		ISTGT_ERRLOG("uctl_writeline() failed\n");
1276 		return NULL;
1277 	}
1278 
1279 	while (1) {
1280 		if (istgt_get_state(uctl->istgt) != ISTGT_STATE_RUNNING) {
1281 			break;
1282 		}
1283 
1284 		/* read from socket */
1285 		rc = istgt_uctl_readline(uctl);
1286 		if (rc == UCTL_CMD_EOF) {
1287 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "uctl_readline() EOF\n");
1288 			break;
1289 		}
1290 		if (rc != UCTL_CMD_OK) {
1291 			ISTGT_ERRLOG("uctl_readline() failed\n");
1292 			break;
1293 		}
1294 		/* execute command */
1295 		rc = istgt_uctl_cmd_execute(uctl);
1296 		if (rc == UCTL_CMD_QUIT) {
1297 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "receive QUIT\n");
1298 			break;
1299 		}
1300 		if (rc == UCTL_CMD_DISCON) {
1301 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request disconnect\n");
1302 			break;
1303 		}
1304 	}
1305 
1306 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "exiting ctlworker\n");
1307 
1308 	close(uctl->sock);
1309 	uctl->sock = -1;
1310 	istgt_free_uctl(uctl);
1311 	return NULL;
1312 }
1313 
1314 static void
istgt_free_uctl(UCTL_Ptr uctl)1315 istgt_free_uctl(UCTL_Ptr uctl)
1316 {
1317 	if (uctl == NULL)
1318 		return;
1319 	xfree(uctl->mediadirectory);
1320 	xfree(uctl->portal.label);
1321 	xfree(uctl->portal.host);
1322 	xfree(uctl->portal.port);
1323 	xfree(uctl->auth.user);
1324 	xfree(uctl->auth.secret);
1325 	xfree(uctl->auth.muser);
1326 	xfree(uctl->auth.msecret);
1327 	xfree(uctl);
1328 }
1329 
1330 int
istgt_create_uctl(ISTGT_Ptr istgt,PORTAL_Ptr portal,int sock,struct sockaddr * sa,socklen_t salen)1331 istgt_create_uctl(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen __attribute__((__unused__)))
1332 {
1333 	char buf[MAX_TMPBUF];
1334 	UCTL_Ptr uctl;
1335 	int rc;
1336 	int i;
1337 
1338 	uctl = xmalloc(sizeof *uctl);
1339 	memset(uctl, 0, sizeof *uctl);
1340 
1341 	uctl->istgt = istgt;
1342 	MTX_LOCK(&istgt->mutex);
1343 	uctl->auth_group = istgt->uctl_auth_group;
1344 	uctl->no_auth = istgt->no_uctl_auth;
1345 	uctl->req_auth = istgt->req_uctl_auth;
1346 	uctl->req_mutual = istgt->req_uctl_auth_mutual;
1347 	uctl->mediadirectory = xstrdup(istgt->mediadirectory);
1348 	MTX_UNLOCK(&istgt->mutex);
1349 
1350 	uctl->portal.label = xstrdup(portal->label);
1351 	uctl->portal.host = xstrdup(portal->host);
1352 	uctl->portal.port = xstrdup(portal->port);
1353 	uctl->portal.tag = portal->tag;
1354 	uctl->portal.sock = -1;
1355 	uctl->sock = sock;
1356 
1357 	uctl->timeout = TIMEOUT_RW;
1358 	uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A;
1359 	uctl->auth.user = NULL;
1360 	uctl->auth.secret = NULL;
1361 	uctl->auth.muser = NULL;
1362 	uctl->auth.msecret = NULL;
1363 	uctl->authenticated = 0;
1364 
1365 	uctl->recvtmpcnt = 0;
1366 	uctl->recvtmpidx = 0;
1367 	uctl->recvtmpsize = sizeof uctl->recvtmp;
1368 	uctl->recvbufsize = sizeof uctl->recvbuf;
1369 	uctl->sendbufsize = sizeof uctl->sendbuf;
1370 	uctl->worksize = sizeof uctl->work;
1371 
1372 	memset(uctl->caddr, 0, sizeof uctl->caddr);
1373 	memset(uctl->saddr, 0, sizeof uctl->saddr);
1374 
1375 	switch (sa->sa_family) {
1376 	case AF_INET6:
1377 		uctl->family = AF_INET6;
1378 		rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr,
1379 		    uctl->caddr, sizeof uctl->caddr);
1380 		if (rc < 0) {
1381 			ISTGT_ERRLOG("istgt_getaddr() failed\n");
1382 			goto error_return;
1383 		}
1384 		break;
1385 	case AF_INET:
1386 		uctl->family = AF_INET;
1387 		rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr,
1388 		    uctl->caddr, sizeof uctl->caddr);
1389 		if (rc < 0) {
1390 			ISTGT_ERRLOG("istgt_getaddr() failed\n");
1391 			goto error_return;
1392 		}
1393 		break;
1394 	default:
1395 		ISTGT_ERRLOG("unsupported family\n");
1396 		goto error_return;
1397 	}
1398 
1399 	if (istgt->nuctl_netmasks != 0) {
1400 		rc = -1;
1401 		for (i = 0; i < istgt->nuctl_netmasks; i++) {
1402 			rc = istgt_lu_allow_netmask(istgt->uctl_netmasks[i], uctl->caddr);
1403 			if (rc > 0) {
1404 				/* OK netmask */
1405 				break;
1406 			}
1407 		}
1408 		if (rc <= 0) {
1409 			ISTGT_WARNLOG("UCTL access denied from %s to (%s:%s)\n",
1410 			    uctl->caddr, uctl->portal.host, uctl->portal.port);
1411 			goto error_return;
1412 		}
1413 	}
1414 
1415 	printf("sock=%d, addr=%s, peer=%s\n",
1416 	    sock, uctl->saddr,
1417 	    uctl->caddr);
1418 
1419 	/* wildcard? */
1420 	if (strcasecmp(uctl->portal.host, "[::]") == 0
1421 	    || strcasecmp(uctl->portal.host, "[*]") == 0) {
1422 		if (uctl->family != AF_INET6) {
1423 			ISTGT_ERRLOG("address family error\n");
1424 			goto error_return;
1425 		}
1426 		snprintf(buf, sizeof buf, "[%s]", uctl->caddr);
1427 		xfree(uctl->portal.host);
1428 		uctl->portal.host = xstrdup(buf);
1429 	} else if (strcasecmp(uctl->portal.host, "0.0.0.0") == 0
1430 	    || strcasecmp(uctl->portal.host, "*") == 0) {
1431 		if (uctl->family != AF_INET) {
1432 			ISTGT_ERRLOG("address family error\n");
1433 			goto error_return;
1434 		}
1435 		snprintf(buf, sizeof buf, "%s", uctl->caddr);
1436 		xfree(uctl->portal.host);
1437 		uctl->portal.host = xstrdup(buf);
1438 	}
1439 
1440 	/* set timeout msec. */
1441 	rc = istgt_set_recvtimeout(uctl->sock, uctl->timeout * 1000);
1442 	if (rc != 0) {
1443 		ISTGT_ERRLOG("istgt_set_recvtimeo() failed\n");
1444 		goto error_return;
1445 	}
1446 	rc = istgt_set_sendtimeout(uctl->sock, uctl->timeout * 1000);
1447 	if (rc != 0) {
1448 		ISTGT_ERRLOG("istgt_set_sendtimeo() failed\n");
1449 		goto error_return;
1450 	}
1451 
1452 	/* create new thread */
1453 #ifdef ISTGT_STACKSIZE
1454 	rc = pthread_create(&uctl->thread, &istgt->attr, &uctlworker, (void *)uctl);
1455 #else
1456 	rc = pthread_create(&uctl->thread, NULL, &uctlworker, (void *)uctl);
1457 #endif
1458 	if (rc != 0) {
1459 		ISTGT_ERRLOG("pthread_create() failed\n");
1460 	error_return:
1461 		xfree(uctl->portal.label);
1462 		xfree(uctl->portal.host);
1463 		xfree(uctl->portal.port);
1464 		xfree(uctl);
1465 		return -1;
1466 	}
1467 	rc = pthread_detach(uctl->thread);
1468 	if (rc != 0) {
1469 		ISTGT_ERRLOG("pthread_detach() failed\n");
1470 		goto error_return;
1471 	}
1472 #ifdef HAVE_PTHREAD_SET_NAME_NP
1473 	pthread_set_name_np(uctl->thread, "uctlthread");
1474 #endif
1475 
1476 	return 0;
1477 }
1478 
1479 int
istgt_uctl_init(ISTGT_Ptr istgt)1480 istgt_uctl_init(ISTGT_Ptr istgt)
1481 {
1482 	CF_SECTION *sp;
1483 	const char *val;
1484 	const char *ag_tag;
1485 	int alloc_len;
1486 	int ag_tag_i;
1487 	int masks;
1488 	int i;
1489 
1490 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_init_uctl_section\n");
1491 
1492 	sp = istgt_find_cf_section(istgt->config, "UnitControl");
1493 	if (sp == NULL) {
1494 		ISTGT_ERRLOG("find_cf_section failed()\n");
1495 		return -1;
1496 	}
1497 
1498 	val = istgt_get_val(sp, "Comment");
1499 	if (val != NULL) {
1500 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val);
1501 	}
1502 
1503 	for (i = 0; ; i++) {
1504 		val = istgt_get_nval(sp, "Netmask", i);
1505 		if (val == NULL)
1506 			break;
1507 	}
1508 	masks = i;
1509 	if (masks > MAX_NETMASK) {
1510 		ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks);
1511 		return -1;
1512 	}
1513 	istgt->nuctl_netmasks = masks;
1514 	alloc_len = sizeof (char *) * masks;
1515 	istgt->uctl_netmasks = xmalloc(alloc_len);
1516 	for (i = 0; i < masks; i++) {
1517 		val = istgt_get_nval(sp, "Netmask", i);
1518 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val);
1519 		istgt->uctl_netmasks[i] = xstrdup(val);
1520 	}
1521 
1522 	val = istgt_get_val(sp, "AuthMethod");
1523 	if (val == NULL) {
1524 		istgt->no_uctl_auth = 0;
1525 		istgt->req_uctl_auth = 0;
1526 	} else {
1527 		istgt->no_uctl_auth = 0;
1528 		for (i = 0; ; i++) {
1529 			val = istgt_get_nmval(sp, "AuthMethod", 0, i);
1530 			if (val == NULL)
1531 				break;
1532 			if (strcasecmp(val, "CHAP") == 0) {
1533 				istgt->req_uctl_auth = 1;
1534 			} else if (strcasecmp(val, "Mutual") == 0) {
1535 				istgt->req_uctl_auth_mutual = 1;
1536 			} else if (strcasecmp(val, "Auto") == 0) {
1537 				istgt->req_uctl_auth = 0;
1538 				istgt->req_uctl_auth_mutual = 0;
1539 			} else if (strcasecmp(val, "None") == 0) {
1540 				istgt->no_uctl_auth = 1;
1541 				istgt->req_uctl_auth = 0;
1542 				istgt->req_uctl_auth_mutual = 0;
1543 			} else {
1544 				ISTGT_ERRLOG("unknown auth\n");
1545 				return -1;
1546 			}
1547 		}
1548 	}
1549 	if (istgt->no_uctl_auth == 0) {
1550 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod None\n");
1551 	} else if (istgt->req_uctl_auth == 0) {
1552 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod Auto\n");
1553 	} else {
1554 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod %s %s\n",
1555 		    istgt->req_uctl_auth ? "CHAP" : "",
1556 		    istgt->req_uctl_auth_mutual ? "Mutual" : "");
1557 	}
1558 
1559 	val = istgt_get_val(sp, "AuthGroup");
1560 	if (val == NULL) {
1561 		istgt->uctl_auth_group = 0;
1562 	} else {
1563 		ag_tag = val;
1564 		if (strcasecmp(ag_tag, "None") == 0) {
1565 			ag_tag_i = 0;
1566 		} else {
1567 			if (strncasecmp(ag_tag, "AuthGroup",
1568 				strlen("AuthGroup")) != 0
1569 			    || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) {
1570 				ISTGT_ERRLOG("auth group error\n");
1571 				return -1;
1572 			}
1573 			if (ag_tag_i == 0) {
1574 				ISTGT_ERRLOG("invalid auth group %d\n", ag_tag_i);
1575 				return -1;
1576 			}
1577 		}
1578 		istgt->uctl_auth_group = ag_tag_i;
1579 	}
1580 	if (istgt->uctl_auth_group == 0) {
1581 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup None\n");
1582 	} else {
1583 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup AuthGroup%d\n",
1584 		    istgt->uctl_auth_group);
1585 	}
1586 
1587 	return 0;
1588 }
1589 
1590 int
istgt_uctl_shutdown(ISTGT_Ptr istgt)1591 istgt_uctl_shutdown(ISTGT_Ptr istgt)
1592 {
1593 	int i;
1594 
1595 	for (i = 0; i < istgt->nuctl_netmasks; i++) {
1596 		xfree(istgt->uctl_netmasks[i]);
1597 	}
1598 	xfree(istgt->uctl_netmasks);
1599 	return 0;
1600 }
1601