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