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