1 /*	$OpenBSD: tokendb.c,v 1.8 2005/09/16 23:47:00 deraadt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1995 Migration Associates Corp. All Rights Reserved
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Berkeley Software Design,
17  *      Inc.
18  * 4. The name of Berkeley Software Design, Inc.  may not be used to endorse
19  *    or promote products derived from this software without specific prior
20  *    written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	BSDI $From: tokendb.c,v 1.1 1996/08/26 20:13:10 prb Exp $
35  */
36 
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/param.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 
43 #include <ctype.h>
44 #include <db.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <grp.h>
48 #include <limits.h>
49 #include <stdio.h>
50 #include <syslog.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include "token.h"
56 #include "tokendb.h"
57 
58 static	DB	*tokendb;
59 
60 /*
61  * Static function prototypes
62  */
63 
64 static	int	tokendb_open(void);
65 static	void	tokendb_close(void);
66 
67 /*
68  * Retrieve a user record from the token database file
69  */
70 
71 int
tokendb_getrec(char * username,TOKENDB_Rec * tokenrec)72 tokendb_getrec(char *username, TOKENDB_Rec *tokenrec)
73 {
74 	DBT	key;
75 	DBT	data;
76 	int	status = 0;
77 
78 	key.data = username;
79 	key.size = strlen(username) + 1;
80 	memset(&data, 0, sizeof(data));
81 
82 	if (tokendb_open())
83 		return(-1);
84 
85 	status = (tokendb->get)(tokendb, &key, &data, 0);
86 	switch (status) {
87 	case 1:
88 		tokendb_close();
89 		return(ENOENT);
90 	case -1:
91 		tokendb_close();
92 		return(-1);
93 	}
94 	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
95 	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
96 		tokenrec->mode = tt->modes & ~TOKEN_RIM;
97 	tokendb_close();
98 	return (0);
99 }
100 
101 /*
102  * Put a user record to the token database file.
103  */
104 
105 int
tokendb_putrec(char * username,TOKENDB_Rec * tokenrec)106 tokendb_putrec(char *username, TOKENDB_Rec *tokenrec)
107 {
108 	DBT	key;
109 	DBT	data;
110 	int	status = 0;
111 
112 	key.data = username;
113 	key.size = strlen(username) + 1;
114 
115 	if (tokenrec->mode)
116 		tokenrec->flags |= TOKEN_USEMODES;
117 	data.data = tokenrec;
118 	data.size = sizeof(TOKENDB_Rec);
119 
120 	if (!tokendb_open()) {
121 		if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
122 			tokendb_close();
123 			return (-1);
124 		}
125 		status = (tokendb->put)(tokendb, &key, &data, 0);
126 	}
127 	tokendb_close();
128 	return (status);
129 }
130 
131 /*
132  * Remove a user record from the token database file.
133  */
134 
135 int
tokendb_delrec(char * username)136 tokendb_delrec(char *username)
137 {
138 	DBT	key;
139 	DBT	data;
140 	int	status = 0;
141 
142 	key.data = username;
143 	key.size = strlen(username) + 1;
144 	memset(&data, 0, sizeof(data));
145 
146 	if (!tokendb_open()) {
147 		if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
148 			tokendb_close();
149 			return (-1);
150 		}
151 		status = (tokendb->del)(tokendb, &key, 0);
152 	}
153 	tokendb_close();
154 	return (status);
155 }
156 
157 /*
158  * Open the token database.  In order to expedite access in
159  * heavily loaded conditions, we employ a N1 lock method.
160  * Updates should be brief, so all locks wait infinitely.
161  * Wait for a read (shared) lock as all updates read first.
162  */
163 
164 static	int
tokendb_open(void)165 tokendb_open(void)
166 {
167 	int	must_set_perms = 0;
168 	int	must_set_mode = 0;
169 	struct	group	*grp;
170 	struct	stat	statb;
171 
172 	if ((grp = getgrnam(TOKEN_GROUP)) == NULL) {
173 		printf("Missing %s group, authentication disabled\n",
174 		    TOKEN_GROUP);
175 		fflush(stdout);
176 		syslog(LOG_ALERT,
177 		    "the %s group is missing, token authentication disabled",
178 		    TOKEN_GROUP);
179 		return (-1);
180 	}
181 
182 	if (stat(tt->db, &statb) < 0) {
183 		if (errno != ENOENT)
184 			return (-1);
185 		must_set_perms++;
186 	} else {
187 		if (statb.st_uid != 0 || statb.st_gid != grp->gr_gid) {
188 #ifdef PARANOID
189 			printf("Authentication disabled\n");
190 			fflush(stdout);
191 			syslog(LOG_ALERT,
192 			    "POTENTIAL COMPROMISE of %s. Owner was %u, "
193 			    "Group was %u", tt->db, statb.st_uid, statb.st_gid);
194 			return (-1);
195 #else
196 			must_set_perms++;
197 #endif
198 		}
199 		if ((statb.st_mode & 0777) != 0640) {
200 #ifdef PARANOID
201 			printf("Authentication disabled\n");
202 			fflush(stdout);
203 			syslog(LOG_ALERT,
204 			    "POTENTIAL COMPROMISE of %s. Mode was %o",
205 			    tt->db, statb.st_mode);
206 			return (-1);
207 #else
208 			must_set_mode++;
209 #endif
210 		}
211 	}
212 	if (!(tokendb =
213 	    dbopen(tt->db, O_CREAT | O_RDWR, 0640, DB_BTREE, 0)) )
214 		return (-1);
215 
216 	if (flock((tokendb->fd)(tokendb), LOCK_SH)) {
217 		(tokendb->close)(tokendb);
218 		return (-1);
219 	}
220 	if (must_set_perms && fchown((tokendb->fd)(tokendb), 0, grp->gr_gid))
221 		syslog(LOG_INFO,
222 		    "Can't set owner/group of %s errno=%m", tt->db);
223 	if (must_set_mode && fchmod((tokendb->fd)(tokendb), 0640))
224 		syslog(LOG_INFO,
225 		    "Can't set mode of %s errno=%m", tt->db);
226 
227 	return (0);
228 }
229 
230 /*
231  * Close the token database.  We are holding an unknown lock.
232  * Release it, then close the db. Since little can be done
233  * about errors, we ignore them.
234  */
235 
236 static	void
tokendb_close(void)237 tokendb_close(void)
238 {
239 	if (tokendb) {
240 		(void)flock((tokendb->fd)(tokendb), LOCK_UN);
241 		(tokendb->close)(tokendb);
242 		tokendb = NULL;
243 	}
244 }
245 
246 /*
247  * Retrieve the first user record from the database, leaving the
248  * database open for the next retrieval. If the march thru the
249  * the database is aborted before end-of-file, the caller should
250  * call tokendb_close to release the read lock.
251  */
252 
253 int
tokendb_firstrec(int reverse_flag,TOKENDB_Rec * tokenrec)254 tokendb_firstrec(int reverse_flag, TOKENDB_Rec *tokenrec)
255 {
256 	DBT	key;
257 	DBT	data;
258 	int	status = 0;
259 
260 	memset(&data, 0, sizeof(data));
261 
262 	if (!tokendb_open()) {
263 		status = (tokendb->seq)(tokendb, &key, &data,
264 				reverse_flag ? R_LAST : R_FIRST);
265 	}
266 	if (status) {
267 		tokendb_close();
268 		return (status);
269 	}
270 	if (!data.data) {
271 		tokendb_close();
272 		return (ENOENT);
273 	}
274 	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
275 	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
276 		tokenrec->mode = tt->modes & ~TOKEN_RIM;
277 	return (0);
278 }
279 
280 /*
281  * Retrieve the next sequential user record from the database. Close
282  * the database only on end-of-file or error.
283  */
284 
285 
286 int
tokendb_nextrec(int reverse_flag,TOKENDB_Rec * tokenrec)287 tokendb_nextrec(int reverse_flag, TOKENDB_Rec *tokenrec)
288 {
289 	DBT	key;
290 	DBT	data;
291 	int	status;
292 
293 	memset(&data, 0, sizeof(data));
294 
295 	status = (tokendb->seq)(tokendb, &key, &data,
296 		reverse_flag ? R_PREV : R_NEXT);
297 
298 	if (status) {
299 		tokendb_close();
300 		return (status);
301 	}
302 	if (!data.data) {
303 		tokendb_close();
304 		return (ENOENT);
305 	}
306 	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
307 	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
308 		tokenrec->mode = tt->modes & ~TOKEN_RIM;
309 	return (0);
310 }
311 
312 /*
313  * Retrieve and lock a user record.  Since there are no facilities in
314  * BSD for record locking, we hack a bit lock into the user record.
315  */
316 
317 int
tokendb_lockrec(char * username,TOKENDB_Rec * tokenrec,unsigned recflags)318 tokendb_lockrec(char *username, TOKENDB_Rec *tokenrec, unsigned recflags)
319 {
320 	DBT	key;
321 	DBT	data;
322 	int	status;
323 
324 	key.data = username;
325 	key.size = strlen(username) + 1;
326 	memset(&data, 0, sizeof(data));
327 
328 	if (tokendb_open())
329 		return(-1);
330 
331 	if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
332 		tokendb_close();
333 		return(-1);
334 	}
335 	switch ((tokendb->get)(tokendb, &key, &data, 0)) {
336 	case 1:
337 		tokendb_close();
338 		return (ENOENT);
339 	case -1:
340 		tokendb_close();
341 		return(-1);
342 	}
343 	memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
344 
345 	if ((tokenrec->flags & TOKEN_LOCKED)||(tokenrec->flags & recflags)) {
346 		tokendb_close();
347 		return(1);
348 	}
349 	data.data = tokenrec;
350 	data.size = sizeof(TOKENDB_Rec);
351 
352 	time(&tokenrec->lock_time);
353 	tokenrec->flags |= recflags;
354 	status = (tokendb->put)(tokendb, &key, &data, 0);
355 	tokendb_close();
356 	if (status)
357 		return(-1);
358 	if ((tokenrec->flags & TOKEN_USEMODES) == 0)
359 		tokenrec->mode = tt->modes & ~TOKEN_RIM;
360 
361 	return(0);
362 }
363 
364