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