util.c revision 201140
1/* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#if 0 38#ifndef lint 39static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95"; 40#endif 41#endif 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/usr.bin/finger/util.c 201140 2009-12-28 20:54:34Z ed $"); 45 46#include <sys/param.h> 47#include <sys/socket.h> 48#include <sys/stat.h> 49#include <ctype.h> 50#include <db.h> 51#include <err.h> 52#include <errno.h> 53#include <fcntl.h> 54#include <paths.h> 55#include <pwd.h> 56#include <stdio.h> 57#include <stdlib.h> 58#include <string.h> 59#define _ULOG_POSIX_NAMES 60#include <ulog.h> 61#include <unistd.h> 62#include "finger.h" 63#include "pathnames.h" 64 65static void find_idle_and_ttywrite(WHERE *); 66static void userinfo(PERSON *, struct passwd *); 67static WHERE *walloc(PERSON *); 68 69int 70match(struct passwd *pw, const char *user) 71{ 72 char *p, *t; 73 char name[1024]; 74 75 if (!strcasecmp(pw->pw_name, user)) 76 return(1); 77 78 /* 79 * XXX 80 * Why do we skip asterisks!?!? 81 */ 82 (void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf)); 83 tbuf[sizeof(tbuf) - 1] = '\0'; 84 if (*p == '*') 85 ++p; 86 87 /* Ampersands get replaced by the login name. */ 88 if ((p = strtok(p, ",")) == NULL) 89 return(0); 90 91 for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 92 if (*t == '&') { 93 (void)strncpy(t, pw->pw_name, 94 sizeof(name) - (t - name)); 95 name[sizeof(name) - 1] = '\0'; 96 while (t < &name[sizeof(name) - 1] && *++t) 97 continue; 98 } else { 99 ++t; 100 } 101 } 102 *t = '\0'; 103 for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL) 104 if (!strcasecmp(p, user)) 105 return(1); 106 return(0); 107} 108 109void 110enter_lastlog(PERSON *pn) 111{ 112 WHERE *w; 113 struct ulog_utmpx *ut; 114 char doit = 0; 115 116 ulog_setutxfile(UTXI_USER, NULL); 117 ut = ulog_getutxuser(pn->name); 118 if ((w = pn->whead) == NULL) 119 doit = 1; 120 else if (ut != NULL && ut->ut_type == USER_PROCESS) { 121 /* if last login is earlier than some current login */ 122 for (; !doit && w != NULL; w = w->next) 123 if (w->info == LOGGEDIN && 124 w->loginat < ut->ut_tv.tv_sec) 125 doit = 1; 126 /* 127 * and if it's not any of the current logins 128 * can't use time comparison because there may be a small 129 * discrepancy since login calls time() twice 130 */ 131 for (w = pn->whead; doit && w != NULL; w = w->next) 132 if (w->info == LOGGEDIN && 133 strcmp(w->tty, ut->ut_line) == 0) 134 doit = 0; 135 } 136 if (ut != NULL && doit) { 137 w = walloc(pn); 138 w->info = LASTLOG; 139 strcpy(w->tty, ut->ut_line); 140 strcpy(w->host, ut->ut_host); 141 w->loginat = ut->ut_tv.tv_sec; 142 } 143 ulog_endutxent(); 144} 145 146void 147enter_where(struct utmpx *ut, PERSON *pn) 148{ 149 WHERE *w; 150 151 w = walloc(pn); 152 w->info = LOGGEDIN; 153 strcpy(w->tty, ut->ut_line); 154 strcpy(w->host, ut->ut_host); 155 w->loginat = ut->ut_tv.tv_sec; 156 find_idle_and_ttywrite(w); 157} 158 159PERSON * 160enter_person(struct passwd *pw) 161{ 162 DBT data, key; 163 PERSON *pn; 164 165 if (db == NULL && 166 (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL) 167 err(1, NULL); 168 169 key.data = pw->pw_name; 170 key.size = strlen(pw->pw_name); 171 172 switch ((*db->get)(db, &key, &data, 0)) { 173 case 0: 174 memmove(&pn, data.data, sizeof pn); 175 return (pn); 176 default: 177 case -1: 178 err(1, "db get"); 179 /* NOTREACHED */ 180 case 1: 181 ++entries; 182 pn = palloc(); 183 userinfo(pn, pw); 184 pn->whead = NULL; 185 186 data.size = sizeof(PERSON *); 187 data.data = &pn; 188 if ((*db->put)(db, &key, &data, 0)) 189 err(1, "db put"); 190 return (pn); 191 } 192} 193 194PERSON * 195find_person(char *name) 196{ 197 struct passwd *pw; 198 199 DBT data, key; 200 PERSON *p; 201 202 if (!db) 203 return(NULL); 204 205 if ((pw = getpwnam(name)) && hide(pw)) 206 return(NULL); 207 208 key.data = name; 209 key.size = strlen(name); 210 211 if ((*db->get)(db, &key, &data, 0)) 212 return (NULL); 213 memmove(&p, data.data, sizeof p); 214 return (p); 215} 216 217PERSON * 218palloc(void) 219{ 220 PERSON *p; 221 222 if ((p = malloc(sizeof(PERSON))) == NULL) 223 err(1, NULL); 224 return(p); 225} 226 227static WHERE * 228walloc(PERSON *pn) 229{ 230 WHERE *w; 231 232 if ((w = malloc(sizeof(WHERE))) == NULL) 233 err(1, NULL); 234 if (pn->whead == NULL) 235 pn->whead = pn->wtail = w; 236 else { 237 pn->wtail->next = w; 238 pn->wtail = w; 239 } 240 w->next = NULL; 241 return(w); 242} 243 244char * 245prphone(char *num) 246{ 247 char *p; 248 int len; 249 static char pbuf[20]; 250 251 /* don't touch anything if the user has their own formatting */ 252 for (p = num; *p; ++p) 253 if (!isdigit(*p)) 254 return(num); 255 len = p - num; 256 p = pbuf; 257 switch(len) { 258 case 11: /* +0-123-456-7890 */ 259 *p++ = '+'; 260 *p++ = *num++; 261 *p++ = '-'; 262 /* FALLTHROUGH */ 263 case 10: /* 012-345-6789 */ 264 *p++ = *num++; 265 *p++ = *num++; 266 *p++ = *num++; 267 *p++ = '-'; 268 /* FALLTHROUGH */ 269 case 7: /* 012-3456 */ 270 *p++ = *num++; 271 *p++ = *num++; 272 *p++ = *num++; 273 break; 274 case 5: /* x0-1234 */ 275 case 4: /* x1234 */ 276 *p++ = 'x'; 277 *p++ = *num++; 278 break; 279 default: 280 return(num); 281 } 282 if (len != 4) { 283 *p++ = '-'; 284 *p++ = *num++; 285 } 286 *p++ = *num++; 287 *p++ = *num++; 288 *p++ = *num++; 289 *p = '\0'; 290 return(pbuf); 291} 292 293static void 294find_idle_and_ttywrite(WHERE *w) 295{ 296 struct stat sb; 297 time_t touched; 298 int error; 299 300 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty); 301 302 error = stat(tbuf, &sb); 303 if (error < 0 && errno == ENOENT) { 304 /* 305 * The terminal listed is not actually a terminal (i.e., 306 * ":0"). This is a failure, so we'll skip printing 307 * out the idle time, which is non-ideal but better 308 * than a bogus warning and idle time. 309 */ 310 w->idletime = -1; 311 return; 312 } else if (error < 0) { 313 warn("%s", tbuf); 314 w->idletime = -1; 315 return; 316 } 317 touched = sb.st_atime; 318 if (touched < w->loginat) { 319 /* tty untouched since before login */ 320 touched = w->loginat; 321 } 322 w->idletime = now < touched ? 0 : now - touched; 323 324#define TALKABLE 0220 /* tty is writable if 220 mode */ 325 w->writable = ((sb.st_mode & TALKABLE) == TALKABLE); 326} 327 328static void 329userinfo(PERSON *pn, struct passwd *pw) 330{ 331 char *p, *t; 332 char *bp, name[1024]; 333 struct stat sb; 334 335 pn->realname = pn->office = pn->officephone = pn->homephone = NULL; 336 337 pn->uid = pw->pw_uid; 338 if ((pn->name = strdup(pw->pw_name)) == NULL) 339 err(1, "strdup failed"); 340 if ((pn->dir = strdup(pw->pw_dir)) == NULL) 341 err(1, "strdup failed"); 342 if ((pn->shell = strdup(pw->pw_shell)) == NULL) 343 err(1, "strdup failed"); 344 345 /* why do we skip asterisks!?!? */ 346 (void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf)); 347 tbuf[sizeof(tbuf) - 1] = '\0'; 348 if (*bp == '*') 349 ++bp; 350 351 /* ampersands get replaced by the login name */ 352 if (!(p = strsep(&bp, ","))) 353 return; 354 for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 355 if (*t == '&') { 356 (void)strncpy(t, pw->pw_name, 357 sizeof(name) - (t - name)); 358 name[sizeof(name) - 1] = '\0'; 359 if (islower(*t)) 360 *t = toupper(*t); 361 while (t < &name[sizeof(name) - 1] && *++t) 362 continue; 363 } else { 364 ++t; 365 } 366 } 367 *t = '\0'; 368 if ((pn->realname = strdup(name)) == NULL) 369 err(1, "strdup failed"); 370 pn->office = ((p = strsep(&bp, ",")) && *p) ? 371 strdup(p) : NULL; 372 pn->officephone = ((p = strsep(&bp, ",")) && *p) ? 373 strdup(p) : NULL; 374 pn->homephone = ((p = strsep(&bp, ",")) && *p) ? 375 strdup(p) : NULL; 376 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name); 377 pn->mailrecv = -1; /* -1 == not_valid */ 378 if (stat(tbuf, &sb) < 0) { 379 if (errno != ENOENT) { 380 warn("%s", tbuf); 381 return; 382 } 383 } else if (sb.st_size != 0) { 384 pn->mailrecv = sb.st_mtime; 385 pn->mailread = sb.st_atime; 386 } 387} 388 389/* 390 * Is this user hiding from finger? 391 * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide). 392 * Nobody can hide from root. 393 */ 394 395int 396hide(struct passwd *pw) 397{ 398 struct stat st; 399 char buf[MAXPATHLEN]; 400 401 if (invoker_root || !pw->pw_dir) 402 return 0; 403 404 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, _PATH_NOFINGER); 405 406 if (stat(buf, &st) == 0) 407 return 1; 408 409 return 0; 410} 411