xref: /titanic_41/usr/src/cmd/ssh/sshd/altprivsep.c (revision 09cb82ca24006b806e9f17e2135eef96364facfe)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  *
21  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 #include "includes.h"
28 #include "atomicio.h"
29 #include "auth.h"
30 #include "bufaux.h"
31 #include "buffer.h"
32 #include "cipher.h"
33 #include "compat.h"
34 #include "dispatch.h"
35 #include "getput.h"
36 #include "kex.h"
37 #include "log.h"
38 #include "mac.h"
39 #include "packet.h"
40 #include "uidswap.h"
41 #include "ssh2.h"
42 #include "sshlogin.h"
43 #include "xmalloc.h"
44 #include "altprivsep.h"
45 #include <fcntl.h>
46 #include <sys/types.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50 
51 extern Kex *xxx_kex;
52 
53 static Buffer to_monitor;
54 static Buffer from_monitor;
55 
56 /*
57  * Sun's Alternative Privilege Separation basics:
58  *
59  * Abstract
60  * --------
61  *
62  * sshd(1M) fork()s and drops privs in the child while retaining privs
63  * in the parent (a.k.a., the monitor).  The unprivileged sshd and the
64  * monitor talk over a pipe using a simple protocol.
65  *
66  * The monitor protocol is all about having the monitor carry out the
67  * only operations that require privileges OR access to privileged
68  * resources.  These are: utmpx/wtmpx record keeping, auditing, and
69  * SSHv2 re-keying.
70  *
71  * Re-Keying
72  * ---------
73  *
74  * Re-keying is the only protocol version specific aspect of sshd in
75  * which the monitor gets involved.
76  *
77  * The monitor processes all SSHv2 re-key protocol packets, but the
78  * unprivileged sshd process does the transport layer crypto for those
79  * packets.
80  *
81  * The monitor and its unprivileged sshd child process treat
82  * SSH_MSG_NEWKEYS SSH2 messages specially: a) the monitor does not call
83  * set_newkeys(), but b) the child asks the monitor for the set of
84  * negotiated algorithms, key, IV and what not for the relevant
85  * transport direction and then calls set_newkeys().
86  *
87  * Monitor Protocol
88  * ----------------
89  *
90  * Monitor IPC message formats are similar to SSHv2 messages, minus
91  * compression, encryption, padding and MACs:
92  *
93  *  - 4 octet message length
94  *  - message data
95  *     - 1 octet message type
96  *     - message data
97  *
98  * In broad strokes:
99  *
100  *  - IPC: pipe, exit(2)/wait4(2)
101  *
102  *  - threads: the monitor and child are single-threaded
103  *
104  *  - monitor main loop: a variant of server_loop2(), for re-keying only
105  *  - unpriv child main loop: server_loop2(), as usual
106  *
107  *  - protocol:
108  *     - key exchange packets are always forwarded as is to the monitor
109  *     - newkeys, record_login(), record_logout() are special packets
110  *     using the packet type range reserved for local extensions
111  *
112  *  - the child drops privs and runs like a normal sshd, except that it
113  *  sets dispatch handlers for key exchange packets that forward the
114  *  packets to the monitor
115  *
116  * Event loops:
117  *
118  *  - all monitor protocols are synchronous: because the SSHv2 rekey
119  *  protocols are synchronous and because the other monitor operations
120  *  are synchronous (or have no replies),
121  *
122  *  - server_loop2() is modified to check the monitor pipe for rekey
123  *  packets to forward to the client
124  *
125  *  - and dispatch handlers are set, upon receipt of KEXINIT (and reset
126  *  when NEWKEYS is sent out) to forward incoming rekey packets to the
127  *  monitor.
128  *
129  *  - the monitor runs an event loop not unlike server_loop2() and runs
130  *  key exchanges almost exactly as a pre-altprivsep sshd would
131  *
132  *  - unpriv sshd exit -> monitor cleanup (including audit logout) and exit
133  *
134  *  - fatal() in monitor -> forcibly shutdown() socket and kill/wait for
135  *  child (so that the audit event for the logout better reflects
136  *  reality -- i.e., logged out means logged out, but for bg jobs)
137  *
138  * Message formats:
139  *
140  *  - key exchange packets/replies forwarded "as is"
141  *
142  *  - all other monitor requests are sent as SSH2_PRIV_MSG_ALTPRIVSEP and have a
143  *  sub-type identifier (one octet)
144  *  - private request sub-types include:
145  *     - get new shared secret from last re-key
146  *     - record login  (utmpx/wtmpx), request data contains three arguments:
147  *     pid, ttyname, program name
148  *     - record logout (utmpx/wtmpx), request data contains one argument: pid
149  *
150  * Reply sub-types include:
151  *
152  *  - NOP (for record_login/logout)
153  *  - new shared secret from last re-key
154  */
155 
156 static int aps_started = 0;
157 static int is_monitor = 0;
158 
159 static pid_t monitor_pid, child_pid;
160 static int pipe_fds[2];
161 static int pipe_fd = -1;
162 static Buffer input_pipe, output_pipe; /* for pipe I/O */
163 
164 static Authctxt *xxx_authctxt;
165 
166 /* Monitor functions */
167 extern void aps_monitor_loop(Authctxt *authctxt, int pipe, pid_t child_pid);
168 static void aps_record_login(void);
169 static void aps_record_logout(void);
170 static void aps_start_rekex(void);
171 
172 /* Altprivsep packet utilities for communication with the monitor */
173 static void	altprivsep_packet_start(u_char);
174 static int	altprivsep_packet_send(void);
175 static int	altprivsep_fwd_packet(u_char type);
176 
177 static int	altprivsep_packet_read(void);
178 static void	altprivsep_packet_read_expect(int type);
179 
180 static void	altprivsep_packet_put_char(int ch);
181 static void	altprivsep_packet_put_int(u_int value);
182 static void	altprivsep_packet_put_cstring(const char *str);
183 static void	altprivsep_packet_put_raw(const void *buf, u_int len);
184 
185 static u_int	 altprivsep_packet_get_char(void);
186 static void	*altprivsep_packet_get_raw(u_int *length_ptr);
187 static void	*altprivsep_packet_get_string(u_int *length_ptr);
188 
189 /*
190  * Start monitor from privileged sshd process.
191  *
192  * Return values are like fork(2); the parent is the monitor.  The caller should
193  * fatal() on error.
194  *
195  * Privileges are dropped, on the unprivileged side, upon success.
196  */
197 pid_t
198 altprivsep_start_monitor(Authctxt *authctxt)
199 {
200 	pid_t pid;
201 	int junk;
202 
203 	if (aps_started || authctxt == NULL || authctxt->pw == NULL)
204 		fatal("Monitor startup failed: missing state");
205 
206 	xxx_authctxt = authctxt;
207 
208 	packet_set_server();
209 
210 	buffer_init(&output_pipe);
211 	buffer_init(&input_pipe);
212 
213 	if (pipe(pipe_fds) != 0) {
214 		error("Monitor startup failure: could not create pipes: %s",
215 			strerror(errno));
216 		return (-1);
217 	}
218 
219 	(void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC);
220 	(void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC);
221 
222 	monitor_pid = getpid();
223 
224 	if ((pid = fork()) > 0) {
225 		/* parent */
226 		child_pid = pid;
227 
228 		debug2("Monitor pid %ld, unprivileged child pid %ld",
229 			monitor_pid, child_pid);
230 
231 		(void) close(pipe_fds[1]);
232 
233 		pipe_fd = pipe_fds[0];
234 
235 		if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
236 			error("fcntl O_NONBLOCK: %.100s", strerror(errno));
237 
238 		/* signal readiness of monitor */
239 		(void) write(pipe_fd, &pid, sizeof (pid));
240 
241 		aps_started = 1;
242 		is_monitor = 1;
243 
244 		debug2("Monitor started");
245 
246 		set_log_txt_prefix("monitor ");
247 
248 		return (pid);
249 
250 	}
251 
252 	if (pid < 0) {
253 		debug2("Monitor startup failure: could not fork unprivileged"
254 			" process:  %s", strerror(errno));
255 		return (pid);
256 	}
257 
258 	/* caller should drop privs */
259 
260 	(void) close(pipe_fds[0]);
261 
262 	pipe_fd = pipe_fds[1];
263 
264 	/* wait for monitor to be ready */
265 	debug2("Waiting for monitor");
266 	(void) read(pipe_fd, &junk, sizeof (junk));
267 	debug2("Monitor signalled readiness");
268 
269 	if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
270 		error("fcntl O_NONBLOCK: %.100s", strerror(errno));
271 
272 	buffer_init(&to_monitor);
273 	buffer_init(&from_monitor);
274 
275 	if (compat20) {
276 		debug3("Setting handler to forward re-key packets to monitor");
277 		dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX,
278 			&altprivsep_rekey);
279 	}
280 
281 	/* AltPrivSep interfaces are set up */
282 	aps_started = 1;
283 	return (pid);
284 }
285 
286 int
287 altprivsep_get_pipe_fd(void)
288 {
289 	return (pipe_fd);
290 }
291 
292 void
293 altprivsep_rekey(int type, u_int32_t seq, void *ctxt)
294 {
295 	Kex *kex = (Kex *)ctxt;
296 
297 	if (kex == NULL)
298 		fatal("Missing key exchange context in unprivileged process");
299 
300 	debug2("Forwarding re-key packet (%d) to monitor", type);
301 
302 	if (type != SSH2_MSG_NEWKEYS)
303 		if (!altprivsep_fwd_packet(type))
304 			fatal("Monitor not responding");
305 
306 	/* tell server_loop2() that we're re-keying */
307 	kex->done = 0;
308 
309 	/* NEWKEYS is special: get the new keys for client->server direction */
310 	if (type == SSH2_MSG_NEWKEYS) {
311 		debug2("Getting new inbound keystate from monitor");
312 		altprivsep_get_newkeys(MODE_IN);
313 		kex->done = 1;
314 	}
315 }
316 
317 void
318 altprivsep_process_input(fd_set *rset)
319 {
320 	void	*data;
321 	int	 type;
322 	u_int	 dlen;
323 
324 	debug2("Reading from pipe to monitor (%d)", pipe_fd);
325 
326 	if (pipe_fd == -1)
327 		return;
328 
329 	if (!FD_ISSET(pipe_fd, rset))
330 		return;
331 
332 	if ((type = altprivsep_packet_read()) == -1)
333 		fatal("Monitor not responding");
334 
335 	if (!compat20)
336 		return; /* shouldn't happen! but be safe */
337 
338 	if (type == 0)
339 		return;	/* EOF -- nothing to do here */
340 
341 	if (type >= SSH2_MSG_MAX)
342 		fatal("Received garbage from monitor");
343 
344 	debug2("Read packet type %d from pipe to monitor", (u_int)type);
345 
346 	if (type == SSH2_PRIV_MSG_ALTPRIVSEP)
347 		return; /* shouldn't happen! */
348 
349 	/* NEWKEYS is special: get the new keys for server->client direction */
350 	if (type == SSH2_MSG_NEWKEYS) {
351 		debug2("Getting new outbound keystate from monitor");
352 		packet_start(SSH2_MSG_NEWKEYS);
353 		packet_send();
354 		altprivsep_get_newkeys(MODE_OUT);
355 		return;
356 	}
357 
358 	data = altprivsep_packet_get_raw(&dlen);
359 
360 	packet_start((u_char)type);
361 
362 	if (data != NULL && dlen > 0)
363 		packet_put_raw(data, dlen);
364 
365 	packet_send();
366 }
367 
368 void
369 altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid)
370 {
371 	aps_monitor_loop(authctxt, pipe_fd, child_pid);
372 }
373 
374 int
375 altprivsep_started(void)
376 {
377 	return (aps_started);
378 }
379 
380 int
381 altprivsep_is_monitor(void)
382 {
383 	return (is_monitor);
384 }
385 
386 /*
387  * A fatal cleanup function to forcibly shutdown the connection socket
388  */
389 void
390 altprivsep_shutdown_sock(void *arg)
391 {
392 	int sock;
393 
394 	if (arg == NULL)
395 		return;
396 
397 	sock = *(int *)arg;
398 
399 	(void) shutdown(sock, SHUT_RDWR);
400 }
401 
402 /* Calls _to_ monitor from unprivileged process */
403 static
404 int
405 altprivsep_fwd_packet(u_char type)
406 {
407 	u_int len;
408 	void  *data;
409 
410 	altprivsep_packet_start(type);
411 	data = packet_get_raw(&len);
412 	altprivsep_packet_put_raw(data, len);
413 
414 	/* packet_send()s any replies from the monitor to the client */
415 	return (altprivsep_packet_send());
416 }
417 
418 extern Newkeys *current_keys[MODE_MAX];
419 
420 /* To be called from packet.c:set_newkeys() before referencing current_keys */
421 void
422 altprivsep_get_newkeys(enum kex_modes mode)
423 {
424 	Newkeys	*newkeys;
425 	Comp	*comp;
426 	Enc	*enc;
427 	Mac	*mac;
428 	u_int	 len;
429 
430 	if (!altprivsep_started())
431 		return;
432 
433 	if (altprivsep_is_monitor())
434 		return; /* shouldn't happen */
435 
436 	/* request new keys */
437 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
438 	altprivsep_packet_put_char(APS_MSG_NEWKEYS_REQ);
439 	altprivsep_packet_put_int((u_int)mode);
440 	altprivsep_packet_send();
441 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
442 	if (altprivsep_packet_get_char() != APS_MSG_NEWKEYS_REP)
443 		fatal("Received garbage from monitor during re-keying");
444 
445 	newkeys = xmalloc(sizeof (*newkeys));
446 	memset(newkeys, 0, sizeof (*newkeys));
447 
448 	enc = &newkeys->enc;
449 	mac = &newkeys->mac;
450 	comp = &newkeys->comp;
451 
452 	/* Cipher name, key, IV */
453 	enc->name = altprivsep_packet_get_string(NULL);
454 	if ((enc->cipher = cipher_by_name(enc->name)) == NULL)
455 		fatal("Monitor negotiated an unknown cipher during re-key");
456 
457 	enc->key = altprivsep_packet_get_string(&enc->key_len);
458 	enc->iv = altprivsep_packet_get_string(&enc->block_size);
459 
460 	/* MAC name */
461 	mac->name = altprivsep_packet_get_string(NULL);
462 	if (mac_init(mac, mac->name) < 0)
463 		fatal("Monitor negotiated an unknown MAC algorithm "
464 			"during re-key");
465 
466 	mac->key = altprivsep_packet_get_string(&len);
467 	if (len > mac->key_len)
468 		fatal("%s: bad mac key length: %d > %d", __func__, len,
469 			mac->key_len);
470 
471 	/* Compression algorithm name */
472 	comp->name = altprivsep_packet_get_string(NULL);
473 	if (strcmp(comp->name, "zlib") != 0 && strcmp(comp->name, "none") != 0)
474 		fatal("Monitor negotiated an unknown compression "
475 			"algorithm during re-key");
476 
477 	comp->type = 0;
478 	comp->enabled = 0; /* forces compression re-init, as per-spec */
479 	if (strcmp(comp->name, "zlib") == 0)
480 		comp->type = 1;
481 
482 	/*
483 	 * Now install new keys
484 	 *
485 	 * For now abuse kex.c/packet.c non-interfaces.  Someday, when
486 	 * the many internal interfaces are parametrized, made reentrant
487 	 * and thread-safe, made more consistent, and when necessary-but-
488 	 * currently-missing interfaces are added then this bit of
489 	 * ugliness can be revisited.
490 	 *
491 	 * The ugliness is in the set_newkeys(), its name and the lack
492 	 * of a (Newkeys *) parameter, which forces us to pass the
493 	 * newkeys through current_keys[mode].  But this saves us some
494 	 * lines of code for now, though not comments.
495 	 *
496 	 * Also, we've abused, in the code above, knowledge of what
497 	 * set_newkeys() expects the current_keys[mode] to contain.
498 	 */
499 	current_keys[mode] = newkeys;
500 	set_newkeys(mode);
501 
502 }
503 
504 void
505 altprivsep_record_login(pid_t pid, const char *ttyname)
506 {
507 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
508 	altprivsep_packet_put_char(APS_MSG_RECORD_LOGIN);
509 	altprivsep_packet_put_int(pid);
510 	altprivsep_packet_put_cstring(ttyname);
511 	altprivsep_packet_send();
512 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
513 }
514 
515 void
516 altprivsep_record_logout(pid_t pid)
517 {
518 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
519 	altprivsep_packet_put_char(APS_MSG_RECORD_LOGOUT);
520 	altprivsep_packet_put_int(pid);
521 	altprivsep_packet_send();
522 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
523 }
524 
525 void
526 altprivsep_start_rekex(void)
527 {
528 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
529 	altprivsep_packet_put_char(APS_MSG_START_REKEX);
530 	altprivsep_packet_send();
531 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
532 }
533 
534 static void aps_send_newkeys(void);
535 
536 /* Monitor side dispatch handler for SSH2_PRIV_MSG_ALTPRIVSEP */
537 /* ARGSUSED */
538 void
539 aps_input_altpriv_msg(int type, u_int32_t seq, void *ctxt)
540 {
541 	u_char req_type;
542 
543 	req_type = packet_get_char();
544 
545 	switch (req_type) {
546 	case APS_MSG_NEWKEYS_REQ:
547 		aps_send_newkeys();
548 		break;
549 	case APS_MSG_RECORD_LOGIN:
550 		aps_record_login();
551 		break;
552 	case APS_MSG_RECORD_LOGOUT:
553 		aps_record_logout();
554 		break;
555 	case APS_MSG_START_REKEX:
556 		aps_start_rekex();
557 		break;
558 	default:
559 		break;
560 	}
561 }
562 
563 /* Monitor-side handlers for APS_MSG_* */
564 static
565 void
566 aps_send_newkeys(void)
567 {
568 	Newkeys *newkeys;
569 	Enc *enc;
570 	Mac *mac;
571 	Comp *comp;
572 	enum kex_modes mode;
573 
574 	/* get direction for which newkeys are wanted */
575 	mode = (enum kex_modes) packet_get_int();
576 	packet_check_eom();
577 
578 	/* get those newkeys */
579 	newkeys = kex_get_newkeys(mode);
580 	enc = &newkeys->enc;
581 	mac = &newkeys->mac;
582 	comp = &newkeys->comp;
583 
584 	/*
585 	 * Negotiated algorithms, client->server and server->client, for
586 	 * cipher, mac and compression.
587 	 */
588 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
589 	packet_put_char(APS_MSG_NEWKEYS_REP);
590 	packet_put_cstring(enc->name);
591 	packet_put_string(enc->key, enc->key_len);
592 	packet_put_string(enc->iv, enc->block_size);
593 	packet_put_cstring(mac->name);
594 	packet_put_string(mac->key, mac->key_len);
595 	packet_put_cstring(comp->name);
596 
597 	packet_send();
598 	free_keys(newkeys);
599 }
600 
601 struct _aps_login_rec {
602 	pid_t			lr_pid;
603 	char			*lr_tty;
604 	struct _aps_login_rec	*next;
605 };
606 
607 typedef struct _aps_login_rec aps_login_rec;
608 
609 static aps_login_rec *aps_login_list = NULL;
610 
611 static
612 void
613 aps_record_login(void)
614 {
615 	aps_login_rec	*new_rec;
616 	struct stat	 sbuf;
617 	size_t		 proc_path_len;
618 	char		*proc_path;
619 
620 	new_rec = xmalloc(sizeof (aps_login_rec));
621 	memset(new_rec, 0, sizeof (aps_login_rec));
622 
623 	new_rec->lr_pid = packet_get_int();
624 	new_rec->lr_tty = packet_get_string(NULL);
625 
626 	proc_path_len = snprintf(NULL, 0, "/proc/%d", new_rec->lr_pid);
627 	proc_path = xmalloc(proc_path_len + 1);
628 	(void) snprintf(proc_path, proc_path_len + 1, "/proc/%d",
629 			new_rec->lr_pid);
630 
631 	if (stat(proc_path, &sbuf) ||
632 	    sbuf.st_uid != xxx_authctxt->pw->pw_uid ||
633 	    stat(new_rec->lr_tty, &sbuf) < 0 ||
634 	    sbuf.st_uid != xxx_authctxt->pw->pw_uid) {
635 		debug2("Spurious record_login request from unprivileged sshd");
636 		xfree(proc_path);
637 		xfree(new_rec->lr_tty);
638 		xfree(new_rec);
639 		return;
640 	}
641 
642 	/* Insert new record on list */
643 	new_rec->next = aps_login_list;
644 	aps_login_list = new_rec;
645 
646 	record_login(new_rec->lr_pid, new_rec->lr_tty, NULL,
647 		xxx_authctxt->user);
648 
649 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
650 	packet_send();
651 
652 	xfree(proc_path);
653 }
654 
655 static
656 void
657 aps_record_logout(void)
658 {
659 	aps_login_rec	**p, *q;
660 	pid_t		 pid;
661 
662 	pid = packet_get_int();
663 	packet_check_eom();
664 
665 	for (p = &aps_login_list; *p != NULL; p = &q->next) {
666 		q = *p;
667 		if (q->lr_pid == pid) {
668 			record_logout(q->lr_pid, q->lr_tty, NULL,
669 				xxx_authctxt->user);
670 
671 			/* dequeue */
672 			*p = q->next;
673 			xfree(q->lr_tty);
674 			xfree(q);
675 			break;
676 		}
677 	}
678 
679 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
680 	packet_send();
681 }
682 
683 static
684 void
685 aps_start_rekex(void)
686 {
687 	/*
688 	 * Send confirmation. We could implement it without that but it doesn't
689 	 * bring any harm to do that and we are consistent with other subtypes
690 	 * of our private SSH2_PRIV_MSG_ALTPRIVSEP message type.
691 	 */
692 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
693 	packet_send();
694 
695 	/*
696 	 * KEX_INIT message could be the one that reached the limit. In that
697 	 * case, it was already forwarded to us from the unnprivileged child,
698 	 * and maybe even acted upon. Obviously we must not send another
699 	 * KEX_INIT message.
700 	 */
701 	if (!(xxx_kex->flags & KEX_INIT_SENT))
702 		kex_send_kexinit(xxx_kex);
703 	else
704 		debug2("rekeying already in progress");
705 }
706 
707 
708 /* Utilities for communication with the monitor */
709 static
710 void
711 altprivsep_packet_start(u_char type)
712 {
713 	buffer_clear(&to_monitor);
714 	buffer_put_char(&to_monitor, type);
715 }
716 static
717 void
718 altprivsep_packet_put_char(int ch)
719 {
720 	buffer_put_char(&to_monitor, ch);
721 }
722 static
723 void
724 altprivsep_packet_put_int(u_int value)
725 {
726 	buffer_put_int(&to_monitor, value);
727 }
728 static
729 void
730 altprivsep_packet_put_cstring(const char *str)
731 {
732 	buffer_put_cstring(&to_monitor, str);
733 }
734 static
735 void
736 altprivsep_packet_put_raw(const void *buf, u_int len)
737 {
738 	buffer_append(&to_monitor, buf, len);
739 }
740 
741 /*
742  * Send a monitor packet to the monitor.  This function is blocking.
743  *
744  * Returns -1 if the monitor pipe has been closed earlier, fatal()s if
745  * there's any other problems.
746  */
747 static
748 int
749 altprivsep_packet_send(void)
750 {
751 	ssize_t len;
752 	u_int32_t plen;	/* packet length */
753 	u_char	plen_buf[sizeof (plen)];
754 	u_char padlen;	/* padding length */
755 	fd_set *setp;
756 
757 	if (pipe_fd == -1)
758 		return (-1);
759 
760 	if ((plen = buffer_len(&to_monitor)) == 0)
761 		return (0);
762 
763 	/*
764 	 * We talk the SSHv2 binary packet protocol to the monitor,
765 	 * using the none cipher, mac and compression algorithms.
766 	 *
767 	 * But, interestingly, the none cipher has a block size of 8
768 	 * bytes, thus we must pad the packet.
769 	 *
770 	 * Also, encryption includes the packet length, so the padding
771 	 * must account for that field.  I.e., (sizeof (packet length) +
772 	 * sizeof (padding length) + packet length + padding length) %
773 	 * block_size must == 0.
774 	 *
775 	 * Also, there must be at least four (4) bytes of padding.
776 	 */
777 	padlen = (8 - ((plen + sizeof (plen) + sizeof (padlen)) % 8)) % 8;
778 	if (padlen < 4)
779 		padlen += 8;
780 
781 	/* packet length counts padding and padding length field */
782 	plen += padlen + sizeof (padlen);
783 
784 	PUT_32BIT(plen_buf, plen);
785 
786 	setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
787 	memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
788 	FD_SET(pipe_fd, setp);
789 
790 	while (select(pipe_fd + 1, NULL, setp, NULL, NULL) == -1) {
791 		if (errno == EAGAIN || errno == EINTR)
792 			continue;
793 		else
794 			goto pipe_gone;
795 	}
796 
797 	xfree(setp);
798 
799 	/* packet length field */
800 	len = atomicio(write, pipe_fd, plen_buf, sizeof (plen));
801 
802 	if (len != sizeof (plen))
803 		goto pipe_gone;
804 
805 	/* padding length field */
806 	len = atomicio(write, pipe_fd, &padlen, sizeof (padlen));
807 
808 	if (len != sizeof (padlen))
809 		goto pipe_gone;
810 
811 	len = atomicio(write, pipe_fd, buffer_ptr(&to_monitor), plen - 1);
812 
813 	if (len != (plen - 1))
814 		goto pipe_gone;
815 
816 	buffer_clear(&to_monitor);
817 
818 	return (1);
819 
820 pipe_gone:
821 
822 	(void) close(pipe_fd);
823 
824 	pipe_fd = -1;
825 
826 	fatal("Monitor not responding");
827 
828 	/* NOTREACHED */
829 	return (0);
830 }
831 
832 /*
833  * Read a monitor packet from the monitor.  This function is blocking.
834  */
835 static
836 int
837 altprivsep_packet_read(void)
838 {
839 	ssize_t len = -1;
840 	u_int32_t plen;
841 	u_char plen_buf[sizeof (plen)];
842 	u_char padlen;
843 	fd_set *setp;
844 
845 	if (pipe_fd == -1)
846 		return (-1);
847 
848 	setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
849 	memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
850 	FD_SET(pipe_fd, setp);
851 
852 	while (select(pipe_fd + 1, setp, NULL, NULL, NULL) == -1) {
853 		if (errno == EAGAIN || errno == EINTR)
854 			continue;
855 		else
856 			goto pipe_gone;
857 	}
858 
859 	xfree(setp);
860 
861 	/* packet length field */
862 	len = atomicio(read, pipe_fd, plen_buf, sizeof (plen));
863 
864 	plen = GET_32BIT(plen_buf);
865 
866 	if (len != sizeof (plen))
867 		goto pipe_gone;
868 
869 	/* padding length field */
870 	len = atomicio(read, pipe_fd, &padlen, sizeof (padlen));
871 
872 	if (len != sizeof (padlen))
873 		goto pipe_gone;
874 
875 	plen -= sizeof (padlen);
876 
877 	buffer_clear(&from_monitor);
878 	buffer_append_space(&from_monitor, plen);
879 
880 	/* packet data + padding */
881 	len = atomicio(read, pipe_fd, buffer_ptr(&from_monitor), plen);
882 
883 	if (len != plen)
884 		goto pipe_gone;
885 
886 	/* remove padding */
887 	if (padlen > 0)
888 		buffer_consume_end(&from_monitor, padlen);
889 
890 	/* packet type */
891 	return (buffer_get_char(&from_monitor));
892 
893 pipe_gone:
894 
895 	(void) close(pipe_fd);
896 
897 	pipe_fd = -1;
898 
899 	if (len < 0)
900 		fatal("Monitor not responding");
901 
902 	debug2("Monitor pipe closed by monitor");
903 	return (0);
904 }
905 
906 static
907 void
908 altprivsep_packet_read_expect(int expected)
909 {
910 	int type;
911 
912 	type = altprivsep_packet_read();
913 
914 	if (type <= 0)
915 		fatal("Monitor not responding");
916 
917 	if (type != expected)
918 		fatal("Protocol error in privilege separation; expected "
919 			"packet type %d, got %d", expected, type);
920 }
921 
922 static
923 u_int
924 altprivsep_packet_get_char(void)
925 {
926 	return (buffer_get_char(&from_monitor));
927 }
928 void
929 *altprivsep_packet_get_raw(u_int *length_ptr)
930 {
931 	if (length_ptr != NULL)
932 		*length_ptr = buffer_len(&from_monitor);
933 
934 	return (buffer_ptr(&from_monitor));
935 }
936 void
937 *altprivsep_packet_get_string(u_int *length_ptr)
938 {
939 	return (buffer_get_string(&from_monitor, length_ptr));
940 }
941