xref: /illumos-gate/usr/src/lib/libnsl/nsl/t_optmgmt.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 1993-2003 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 		/* SVr4.0 1.3.4.1	*/
34 
35 #include "mt.h"
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <rpc/trace.h>
40 #include <errno.h>
41 #include <sys/stream.h>
42 #define	_SUN_TPI_VERSION 2
43 #include <sys/tihdr.h>
44 #include <sys/timod.h>
45 #include <xti.h>
46 #include <signal.h>
47 #include <syslog.h>
48 #include <stropts.h>
49 #include "tx.h"
50 
51 /*
52  * The following is based on XTI standard.
53  */
54 #define	ALIGN_XTI_opthdr_size	(sizeof (t_uscalar_t))
55 
56 #define	ROUNDUP_XTI_opthdr(p)	(((p) +\
57 		(ALIGN_XTI_opthdr_size-1)) & ~(ALIGN_XTI_opthdr_size-1))
58 #define	ISALIGNED_XTI_opthdr(p)	\
59 	(((ulong_t)(p) & (ALIGN_XTI_opthdr_size - 1)) == 0)
60 
61 int
62 _tx_optmgmt(
63 	int fd,
64 	const struct t_optmgmt *req,
65 	struct t_optmgmt *ret,
66 	int api_semantics
67 )
68 {
69 	int size, sv_errno;
70 	struct strbuf ctlbuf;
71 	struct T_optmgmt_req *optreq;
72 	struct T_optmgmt_ack *optack;
73 	struct _ti_user *tiptr;
74 	sigset_t mask;
75 	int didalloc, retlen;
76 	struct t_opthdr *opt, *next_opt;
77 	struct t_opthdr *opt_start, *opt_end;
78 	t_uscalar_t first_opt_level;
79 	t_scalar_t optlen;
80 
81 	trace2(TR_t_optmgmt, 0, fd);
82 	if ((tiptr = _t_checkfd(fd, 0, api_semantics)) == NULL) {
83 		sv_errno = errno;
84 		trace2(TR_t_optmgmt, 1, fd);
85 		errno = sv_errno;
86 		return (-1);
87 	}
88 
89 	/*
90 	 * We block all signals during the TI_OPTMGMT operation
91 	 * as option change being done could potentially be a
92 	 * non-idempotent operation.
93 	 * Note that sig_mutex_lock() only defers signals, it does not
94 	 * block them, so interruptible syscalls could still get EINTR.
95 	 */
96 	(void) thr_sigsetmask(SIG_SETMASK, &fillset, &mask);
97 	sig_mutex_lock(&tiptr->ti_lock);
98 
99 	/*
100 	 * Acquire buf for use in sending/receiving of the message.
101 	 * Note: assumes (correctly) that ti_ctlsize is large enough
102 	 * to hold sizeof (struct T_bind_req)
103 	 */
104 	if (_t_acquire_ctlbuf(tiptr, &ctlbuf, &didalloc) < 0) {
105 		sv_errno = errno;
106 		sig_mutex_unlock(&tiptr->ti_lock);
107 		(void) thr_sigsetmask(SIG_SETMASK, &mask, NULL);
108 		trace2(TR_t_optmgmt, 1, fd);
109 		errno = sv_errno;
110 		return (-1);
111 	}
112 
113 	/*
114 	 * effective option length in local variable "optlen"
115 	 * Note: can change for XTI for T_ALLOPT. XTI spec states
116 	 * that options after the T_ALLOPT option are to be ignored
117 	 * therefore we trncate the option buffer there and modify
118 	 * the effective length accordingly later.
119 	 */
120 	optlen = req->opt.len;
121 
122 	if (_T_IS_XTI(api_semantics) && (optlen > 0)) {
123 		/*
124 		 * Verify integrity of option buffer according to
125 		 * XTI t_optmgmt() semantics.
126 		 */
127 
128 		if (req->opt.buf == NULL ||
129 		    optlen < (t_scalar_t)sizeof (struct t_opthdr)) {
130 			/* option buffer should atleast have an t_opthdr */
131 			t_errno = TBADOPT;
132 			goto err_out;
133 		}
134 
135 		opt_start = (struct t_opthdr *)req->opt.buf;
136 
137 		/*
138 		 * XXX We interpret that an option has to start on an
139 		 * aligned buffer boundary. This is not very explcit in
140 		 * XTI spec in text but the picture in Section 6.2 shows
141 		 * "opt.buf" at start of buffer and in combination with
142 		 * text can be construed to be restricting it to start
143 		 * on an aligned boundary. [Whether similar restriction
144 		 * applies to output buffer "ret->opt.buf" is an "interesting
145 		 * question" but we ignore it for now as that is the problem
146 		 * for the application not our implementation which will
147 		 * does not enforce any alignment requirement.]
148 		 *
149 		 * If start of buffer is not aligned, we signal an error.
150 		 */
151 		if (! (ISALIGNED_XTI_opthdr(opt_start))) {
152 			t_errno = TBADOPT;
153 			goto err_out;
154 		}
155 
156 		opt_end = (struct t_opthdr *)((char *)opt_start +
157 						optlen);
158 
159 		/*
160 		 * Make sure we have enough in the message to dereference
161 		 * the option header.
162 		 */
163 		if ((uchar_t *)opt_start + sizeof (struct t_opthdr)
164 		    > (uchar_t *)opt_end) {
165 			t_errno = TBADOPT;
166 			goto err_out;
167 		}
168 		/*
169 		 * If there are multiple options, they all have to be
170 		 * the same level (so says XTI semantics).
171 		 */
172 		first_opt_level = opt_start->level;
173 
174 		for (opt = opt_start; opt < opt_end; opt = next_opt) {
175 			/*
176 			 * Make sure we have enough in the message to
177 			 * dereference the option header.
178 			 */
179 			if ((uchar_t *)opt_start + sizeof (struct t_opthdr)
180 			    > (uchar_t *)opt_end) {
181 				t_errno = TBADOPT;
182 				goto err_out;
183 			}
184 			/*
185 			 * We now compute pointer to next option in buffer
186 			 * 'next_opt' the next_opt computation above below
187 			 * 'opt->len' initialized by application which cannot
188 			 * be trusted. The usual value too large will be
189 			 * captured by the loop termination condition above.
190 			 * We check for the following which it will miss.
191 			 *	(1)pointer space wraparound arithmetic overflow
192 			 *	(2)last option in buffer with 'opt->len' being
193 			 *	  too large
194 			 *	(only reason 'next_opt' should equal or exceed
195 			 *	'opt_end' for last option is roundup unless
196 			 *	length is too-large/invalid)
197 			 *	(3) we also enforce the XTI restriction that
198 			 *	   all options in the buffer have to be the
199 			 *	   same level.
200 			 */
201 			next_opt = (struct t_opthdr *)((uchar_t *)opt +
202 			    ROUNDUP_XTI_opthdr(opt->len));
203 
204 			if ((uchar_t *)next_opt < (uchar_t *)opt || /* (1) */
205 			    ((next_opt >= opt_end) &&
206 				(((uchar_t *)next_opt - (uchar_t *)opt_end) >=
207 				    ALIGN_XTI_opthdr_size)) || /* (2) */
208 			    (opt->level != first_opt_level)) { /* (3) */
209 				t_errno = TBADOPT;
210 				goto err_out;
211 			}
212 
213 			/*
214 			 * XTI semantics: options in the buffer after
215 			 * the T_ALLOPT option can be ignored
216 			 */
217 			if (opt->name == T_ALLOPT) {
218 				if (next_opt < opt_end) {
219 					/*
220 					 * there are options following, ignore
221 					 * them and truncate input
222 					 */
223 					optlen = (t_scalar_t)((uchar_t *)
224 					    next_opt - (uchar_t *)opt_start);
225 					opt_end = next_opt;
226 				}
227 			}
228 		}
229 	}
230 
231 	optreq = (struct T_optmgmt_req *)ctlbuf.buf;
232 	if (_T_IS_XTI(api_semantics))
233 		optreq->PRIM_type = T_OPTMGMT_REQ;
234 	else
235 		optreq->PRIM_type = T_SVR4_OPTMGMT_REQ;
236 
237 	optreq->OPT_length = optlen;
238 	optreq->OPT_offset = 0;
239 	optreq->MGMT_flags = req->flags;
240 	size = (int)sizeof (struct T_optmgmt_req);
241 
242 	if (optlen) {
243 		if (_t_aligned_copy(&ctlbuf, optlen, size,
244 		    req->opt.buf, &optreq->OPT_offset) < 0) {
245 			/*
246 			 * Aligned copy will overflow buffer allocated
247 			 * based on maximum transport option size information
248 			 */
249 			t_errno = TBADOPT;
250 			goto err_out;
251 		}
252 		size = optreq->OPT_offset + optreq->OPT_length;
253 	}
254 
255 	if (_t_do_ioctl(fd, ctlbuf.buf, size, TI_OPTMGMT, &retlen) < 0)
256 		goto err_out;
257 
258 	if (retlen < (int)sizeof (struct T_optmgmt_ack)) {
259 		t_errno = TSYSERR;
260 		errno = EIO;
261 		goto err_out;
262 	}
263 
264 	optack = (struct T_optmgmt_ack *)ctlbuf.buf;
265 
266 	if (_T_IS_TLI(api_semantics) || ret->opt.maxlen > 0) {
267 		if (TLEN_GT_NLEN(optack->OPT_length, ret->opt.maxlen)) {
268 			t_errno = TBUFOVFLW;
269 			goto err_out;
270 		}
271 		(void) memcpy(ret->opt.buf,
272 		    (char *)(ctlbuf.buf + optack->OPT_offset),
273 		    (unsigned int) optack->OPT_length);
274 		ret->opt.len = optack->OPT_length;
275 	}
276 
277 	/*
278 	 * Note: TPI is not clear about what really is carries in the
279 	 * T_OPTMGMT_ACK MGMT_flags fields. For T_OPTMGMT_ACK in response
280 	 * to T_SVR4_OPTMGMT_REQ, the Internet protocols in Solaris 2.X return
281 	 * the result code only (T_SUCCESS). For T_OPTMGMT_ACK in response
282 	 * to T_OPTMGMT_REQ, currently "worst status" code required for
283 	 * XTI is carried from the set of options OR'd with request flag.
284 	 * (This can change in future and "worst status" computation done
285 	 * with a scan in this routine.
286 	 *
287 	 * Note: Even for T_OPTMGMT_ACK is response to T_SVR4_OPTMGMT_REQ,
288 	 * removing request flag should be OK though it will not be set.
289 	 */
290 	ret->flags = optack->MGMT_flags & ~req->flags;
291 
292 	/*
293 	 * NOTE:
294 	 * There is no real change of state in state table for option
295 	 * management. The state change macro is used below only for its
296 	 * debugging and logging capabilities.
297 	 * The TLI "(mis)feature" (option management only in T_IDLE state)
298 	 * has been deprecated in XTI and our state table reflect updated for
299 	 * both TLI and XTI to reflect that.
300 	 * TLI semantics can be enforced by the transport providers that
301 	 * desire it at TPI level.
302 	 * There is no need to enforce this in the library since
303 	 * sane transport providers that do allow it (e.g TCP and it *needs*
304 	 * to allow it) should be allowed to work fine.
305 	 * The only transport providers that return TOUTSTATE for TLI
306 	 * t_optmgmt() are the drivers used for conformance testing to the
307 	 * broken TLI standard.
308 	 * These are /dev/{ticots,ticotsord,ticlts} used by the Sparc ABI test
309 	 * suite. Others are /dev/{tivc,tidg} used by the SVVS test suite.
310 	 */
311 
312 	_T_TX_NEXTSTATE(T_OPTMGMT, tiptr,
313 	    "t_optmgmt: invalid state event T_OPTMGMT");
314 
315 	if (didalloc)
316 		free(ctlbuf.buf);
317 	else
318 		tiptr->ti_ctlbuf = ctlbuf.buf;
319 	sig_mutex_unlock(&tiptr->ti_lock);
320 	(void) thr_sigsetmask(SIG_SETMASK, &mask, NULL);
321 	trace2(TR_t_optmgmt, 1, fd);
322 	return (0);
323 	/* NOTREACHED */
324 
325 err_out:
326 	sv_errno = errno;
327 	if (didalloc)
328 		free(ctlbuf.buf);
329 	else
330 		tiptr->ti_ctlbuf = ctlbuf.buf;
331 	sig_mutex_unlock(&tiptr->ti_lock);
332 	(void) thr_sigsetmask(SIG_SETMASK, &mask, NULL);
333 	trace2(TR_t_optmgmt, 1, fd);
334 	errno = sv_errno;
335 	return (-1);
336 }
337