xref: /illumos-gate/usr/src/cmd/rpcbind/warmstart.c (revision 355b4669e025ff377602b6fc7caaf30dbc218371)
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 /*
23  * warmstart.c
24  * Allows for gathering of registrations from a earlier dumped file.
25  *
26  * Copyright 1990,2002-2003 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <errno.h>
34 #include <rpc/rpc.h>
35 #include <rpc/rpcb_prot.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #ifdef PORTMAP
39 #include <netinet/in.h>
40 #include <rpc/pmap_prot.h>
41 #endif
42 #include "rpcbind.h"
43 #include <syslog.h>
44 #include <unistd.h>
45 #include <rpcsvc/daemon_utils.h>
46 
47 /* These files keep the pmap_list and rpcb_list in XDR format */
48 static const char rpcbfile[] = DAEMON_DIR "/rpcbind.file";
49 #ifdef PORTMAP
50 static const char pmapfile[] = DAEMON_DIR "/portmap.file";
51 #endif
52 
53 
54 static bool_t write_struct();
55 static bool_t read_struct();
56 
57 FILE *
58 open_tmp_file(filename)
59 	char *filename;
60 {
61 	int fd;
62 	FILE *fp;
63 
64 	/*
65 	 * Remove any existing files, and create a new one.
66 	 * Ensure that rpcbind is not forced to overwrite
67 	 * a file pointed to by a symbolic link created
68 	 * by an attacker.
69 	 * Use open O_CREAT|O_EXCL so file is not created
70 	 * between unlink() and open() operation.
71 	 */
72 	if (unlink(filename) == -1) {
73 		if (errno != ENOENT)
74 			return (NULL);
75 	}
76 	fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
77 	if (fd == -1)
78 		return (NULL);
79 	fp = fdopen(fd, "w");
80 	if (fp == NULL) {
81 		close(fd);
82 		return (NULL);
83 	}
84 
85 	return (fp);
86 }
87 
88 static bool_t
89 write_struct(filename, structproc, list)
90 	char *filename;
91 	xdrproc_t structproc;
92 	void *list;
93 {
94 	FILE *fp;
95 	XDR xdrs;
96 
97 	fp = open_tmp_file(filename);
98 	if (fp == NULL) {
99 		int i;
100 
101 		for (i = 0; i < 10; i++)
102 			close(i);
103 		fp = open_tmp_file(filename);
104 		if (fp == NULL) {
105 			syslog(LOG_ERR,
106 				"cannot open file = %s for writing", filename);
107 			syslog(LOG_ERR, "cannot save any registration");
108 			return (FALSE);
109 		}
110 	}
111 	xdrstdio_create(&xdrs, fp, XDR_ENCODE);
112 
113 	if (structproc(&xdrs, list) == FALSE) {
114 		syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename);
115 		fclose(fp);
116 		return (FALSE);
117 	}
118 	XDR_DESTROY(&xdrs);
119 	fclose(fp);
120 	return (TRUE);
121 }
122 
123 static bool_t
124 read_struct(filename, structproc, list)
125 	char *filename;
126 	xdrproc_t structproc;
127 	void *list;
128 {
129 	int fd;
130 	FILE *fp = NULL;
131 	XDR xdrs;
132 	struct stat sbuf_fstat, sbuf_lstat;
133 
134 	fd = open(filename, O_RDONLY, 0600);
135 	if (fd == -1) {
136 		fprintf(stderr,
137 		"rpcbind: cannot open file = %s for reading\n", filename);
138 		goto error;
139 	}
140 	fp = fdopen(fd, "r");
141 	if (fp == NULL) {
142 		close(fd);
143 		fprintf(stderr,
144 		"rpcbind: cannot open file = %s for reading\n", filename);
145 		goto error;
146 	}
147 	if (fstat(fd, &sbuf_fstat) != 0) {
148 		fprintf(stderr,
149 		"rpcbind: cannot stat file = %s for reading\n", filename);
150 		goto error;
151 	}
152 	if (sbuf_fstat.st_uid != DAEMON_UID ||
153 	    (!S_ISREG(sbuf_fstat.st_mode)) ||
154 	    (sbuf_fstat.st_mode & S_IRWXG) ||
155 	    (sbuf_fstat.st_mode & S_IRWXO) ||
156 	    (sbuf_fstat.st_nlink != 1)) {
157 		fprintf(stderr,
158 		"rpcbind: invalid permissions on file = %s for reading\n",
159 			filename);
160 		goto error;
161 	}
162 	/*
163 	 * Make sure that the pathname for fstat and lstat is the same and
164 	 * that it's not a link.  An attacker can create symbolic or
165 	 * hard links and use them to gain unauthorised access to the
166 	 * system when rpcbind aborts or terminates on SIGINT or SIGTERM.
167 	 */
168 	if (lstat(filename, &sbuf_lstat) != 0) {
169 		fprintf(stderr,
170 		"rpcbind: cannot lstat file = %s for reading\n", filename);
171 		goto error;
172 	}
173 	if (sbuf_lstat.st_uid != DAEMON_UID ||
174 	    (!S_ISREG(sbuf_lstat.st_mode)) ||
175 	    (sbuf_lstat.st_mode & S_IRWXG) ||
176 	    (sbuf_lstat.st_mode & S_IRWXO) ||
177 	    (sbuf_lstat.st_nlink != 1) ||
178 	    (sbuf_fstat.st_dev != sbuf_lstat.st_dev) ||
179 	    (sbuf_fstat.st_ino != sbuf_lstat.st_ino)) {
180 		fprintf(stderr,
181 		"rpcbind: invalid lstat permissions on file = %s for reading\n",
182 			filename);
183 		goto error;
184 	}
185 	xdrstdio_create(&xdrs, fp, XDR_DECODE);
186 
187 	if (structproc(&xdrs, list) == FALSE) {
188 		fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename);
189 		goto error;
190 	}
191 	XDR_DESTROY(&xdrs);
192 	fclose(fp);
193 	return (TRUE);
194 
195 error:	fprintf(stderr, "rpcbind: will start from scratch\n");
196 	if (fp != NULL)
197 		fclose(fp);
198 	return (FALSE);
199 }
200 
201 void
202 write_warmstart(void)
203 {
204 	(void) write_struct(rpcbfile, xdr_rpcblist_ptr, &list_rbl);
205 #ifdef PORTMAP
206 	(void) write_struct(pmapfile, xdr_pmaplist_ptr, &list_pml);
207 #endif
208 
209 }
210 
211 void
212 read_warmstart(void)
213 {
214 	rpcblist_ptr tmp_rpcbl = NULL;
215 #ifdef PORTMAP
216 	pmaplist_ptr tmp_pmapl = NULL;
217 #endif
218 	int ok1, ok2 = TRUE;
219 
220 	ok1 = read_struct(rpcbfile, xdr_rpcblist_ptr, &tmp_rpcbl);
221 	if (ok1 == FALSE)
222 		return;
223 #ifdef PORTMAP
224 	ok2 = read_struct(pmapfile, xdr_pmaplist_ptr, &tmp_pmapl);
225 #endif
226 	if (ok2 == FALSE) {
227 		xdr_free((xdrproc_t)xdr_rpcblist_ptr, (char *)&tmp_rpcbl);
228 		return;
229 	}
230 	xdr_free((xdrproc_t)xdr_rpcblist_ptr, (char *)&list_rbl);
231 	list_rbl = tmp_rpcbl;
232 #ifdef PORTMAP
233 	xdr_free((xdrproc_t)xdr_pmaplist_ptr, (char *)&list_pml);
234 	list_pml = (pmaplist *)tmp_pmapl;
235 #endif
236 }
237