util.c revision 112987
1122679Sume/* 278064Sume * Copyright (c) 1989, 1993 378064Sume * The Regents of the University of California. All rights reserved. 478064Sume * 578064Sume * This code is derived from software contributed to Berkeley by 678064Sume * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. 778064Sume * 878064Sume * Redistribution and use in source and binary forms, with or without 978064Sume * modification, are permitted provided that the following conditions 1078064Sume * are met: 1178064Sume * 1. Redistributions of source code must retain the above copyright 1278064Sume * notice, this list of conditions and the following disclaimer. 1378064Sume * 2. Redistributions in binary form must reproduce the above copyright 1478064Sume * notice, this list of conditions and the following disclaimer in the 1578064Sume * documentation and/or other materials provided with the distribution. 1678064Sume * 3. All advertising materials mentioning features or use of this software 1778064Sume * must display the following acknowledgement: 1878064Sume * This product includes software developed by the University of 1978064Sume * California, Berkeley and its contributors. 2078064Sume * 4. Neither the name of the University nor the names of its contributors 2178064Sume * may be used to endorse or promote products derived from this software 2278064Sume * without specific prior written permission. 2378064Sume * 2478064Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2578064Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2678064Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2778064Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2878064Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2978064Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3078064Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3178064Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3278064Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3378064Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3478064Sume * SUCH DAMAGE. 3578064Sume */ 3678064Sume 3778064Sume#if 0 3878064Sume#ifndef lint 3978064Sumestatic char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95"; 4078064Sume#endif 4178064Sume#endif 4278064Sume 4378064Sume#include <sys/cdefs.h> 4478064Sume__FBSDID("$FreeBSD: head/usr.bin/finger/util.c 112987 2003-04-02 20:22:29Z rwatson $"); 4578064Sume 4678064Sume#include <sys/param.h> 4778064Sume#include <sys/socket.h> 4878064Sume#include <sys/stat.h> 4978064Sume#include <ctype.h> 50173412Skevlo#include <db.h> 51173412Skevlo#include <err.h> 5278064Sume#include <errno.h> 53173412Skevlo#include <fcntl.h> 54173412Skevlo#include <paths.h> 5578064Sume#include <pwd.h> 5678064Sume#include <stdio.h> 5778064Sume#include <stdlib.h> 5878064Sume#include <string.h> 5978064Sume#include <unistd.h> 6078064Sume#include <utmp.h> 61122679Sume#include "finger.h" 6278064Sume#include "pathnames.h" 63122679Sume 6478064Sumestatic void find_idle_and_ttywrite(WHERE *); 6578064Sumestatic void userinfo(PERSON *, struct passwd *); 6678064Sumestatic WHERE *walloc(PERSON *); 6778064Sume 68122679Sumeint 69122679Sumematch(struct passwd *pw, const char *user) 7078064Sume{ 7178064Sume char *p, *t; 7278064Sume char name[1024]; 7378064Sume 7478064Sume if (!strcasecmp(pw->pw_name, user)) 7578064Sume return(1); 7678064Sume 7778064Sume /* 7878064Sume * XXX 7978064Sume * Why do we skip asterisks!?!? 8078064Sume */ 8178064Sume (void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf)); 8278064Sume tbuf[sizeof(tbuf) - 1] = '\0'; 8378064Sume if (*p == '*') 8478064Sume ++p; 8578064Sume 8678064Sume /* Ampersands get replaced by the login name. */ 8778064Sume if ((p = strtok(p, ",")) == NULL) 8878064Sume return(0); 8978064Sume 9078064Sume for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 9178064Sume if (*t == '&') { 9278064Sume (void)strncpy(t, pw->pw_name, 9378064Sume sizeof(name) - (t - name)); 9478064Sume name[sizeof(name) - 1] = '\0'; 9578064Sume while (t < &name[sizeof(name) - 1] && *++t) 9678064Sume continue; 9778064Sume } else { 9878064Sume ++t; 9978064Sume } 10078064Sume } 10178064Sume *t = '\0'; 10278064Sume for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL) 10378064Sume if (!strcasecmp(p, user)) 10478064Sume return(1); 10578064Sume return(0); 10678064Sume} 10778064Sume 10878064Sumevoid 10978064Sumeenter_lastlog(PERSON *pn) 11078064Sume{ 11178064Sume WHERE *w; 11278064Sume static int opened, fd; 11378064Sume struct lastlog ll; 11478064Sume char doit = 0; 11578064Sume 11678064Sume /* some systems may not maintain lastlog, don't report errors. */ 11778064Sume if (!opened) { 11878064Sume fd = open(_PATH_LASTLOG, O_RDONLY, 0); 11978064Sume opened = 1; 12078064Sume } 12178064Sume if (fd == -1 || 12278064Sume lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) != 12378064Sume (long)pn->uid * sizeof(ll) || 124122679Sume read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) { 12578064Sume /* as if never logged in */ 12678064Sume ll.ll_line[0] = ll.ll_host[0] = '\0'; 12778064Sume ll.ll_time = 0; 12878064Sume } 12995023Ssuz if ((w = pn->whead) == NULL) 13095023Ssuz doit = 1; 13178064Sume else if (ll.ll_time != 0) { 13278064Sume /* if last login is earlier than some current login */ 13378064Sume for (; !doit && w != NULL; w = w->next) 13478064Sume if (w->info == LOGGEDIN && w->loginat < ll.ll_time) 13578064Sume doit = 1; 13678064Sume /* 137122679Sume * and if it's not any of the current logins 13878064Sume * can't use time comparison because there may be a small 13978064Sume * discrepancy since login calls time() twice 14078064Sume */ 14178064Sume for (w = pn->whead; doit && w != NULL; w = w->next) 14278064Sume if (w->info == LOGGEDIN && 14378064Sume strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0) 14478064Sume doit = 0; 14578064Sume } 14678064Sume if (doit) { 14778064Sume w = walloc(pn); 14878064Sume w->info = LASTLOG; 14978064Sume bcopy(ll.ll_line, w->tty, UT_LINESIZE); 15078064Sume w->tty[UT_LINESIZE] = 0; 15178064Sume bcopy(ll.ll_host, w->host, UT_HOSTSIZE); 15278064Sume w->host[UT_HOSTSIZE] = 0; 15378064Sume w->loginat = ll.ll_time; 15478064Sume } 15578064Sume} 15678064Sume 15778064Sumevoid 15878064Sumeenter_where(struct utmp *ut, PERSON *pn) 15978064Sume{ 16078064Sume WHERE *w; 16178064Sume 16278064Sume w = walloc(pn); 16378064Sume w->info = LOGGEDIN; 16478064Sume bcopy(ut->ut_line, w->tty, UT_LINESIZE); 16578064Sume w->tty[UT_LINESIZE] = 0; 16678064Sume bcopy(ut->ut_host, w->host, UT_HOSTSIZE); 16778064Sume w->host[UT_HOSTSIZE] = 0; 16878064Sume w->loginat = (time_t)ut->ut_time; 16978064Sume find_idle_and_ttywrite(w); 17078064Sume} 17178064Sume 17278064SumePERSON * 17378064Sumeenter_person(struct passwd *pw) 17478064Sume{ 17578064Sume DBT data, key; 17678064Sume PERSON *pn; 17778064Sume 17878064Sume if (db == NULL && 17978064Sume (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL) 18078064Sume err(1, NULL); 18178064Sume 18278064Sume key.data = pw->pw_name; 18378064Sume key.size = strlen(pw->pw_name); 18478064Sume 18578064Sume switch ((*db->get)(db, &key, &data, 0)) { 18678064Sume case 0: 18778064Sume memmove(&pn, data.data, sizeof pn); 18878064Sume return (pn); 189122679Sume default: 19078064Sume case -1: 19178064Sume err(1, "db get"); 19278064Sume /* NOTREACHED */ 19378064Sume case 1: 19478064Sume ++entries; 19578064Sume pn = palloc(); 19678064Sume userinfo(pn, pw); 19778064Sume pn->whead = NULL; 19878064Sume 19978064Sume data.size = sizeof(PERSON *); 20078064Sume data.data = &pn; 20178064Sume if ((*db->put)(db, &key, &data, 0)) 20278064Sume err(1, "db put"); 20378064Sume return (pn); 20478064Sume } 20578064Sume} 20678064Sume 20778064SumePERSON * 20878064Sumefind_person(const char *name) 20978064Sume{ 21078064Sume struct passwd *pw; 21178064Sume 21278064Sume int cnt; 21378064Sume DBT data, key; 21478064Sume PERSON *p; 21578064Sume char buf[UT_NAMESIZE + 1]; 21678064Sume 21778064Sume if (!db) 21878064Sume return(NULL); 21978064Sume 22078064Sume if ((pw = getpwnam(name)) && hide(pw)) 22178064Sume return(NULL); 22278064Sume 22378064Sume /* Name may be only UT_NAMESIZE long and not NUL terminated. */ 22478064Sume for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt) 22578064Sume buf[cnt] = *name; 22678064Sume buf[cnt] = '\0'; 22778064Sume key.data = buf; 22878064Sume key.size = cnt; 22978064Sume 23078064Sume if ((*db->get)(db, &key, &data, 0)) 23178064Sume return (NULL); 23278064Sume memmove(&p, data.data, sizeof p); 23378064Sume return (p); 23478064Sume} 23578064Sume 23678064SumePERSON * 23778064Sumepalloc(void) 23878064Sume{ 23978064Sume PERSON *p; 24078064Sume 24178064Sume if ((p = malloc(sizeof(PERSON))) == NULL) 24278064Sume err(1, NULL); 24378064Sume return(p); 24478064Sume} 24578064Sume 24678064Sumestatic WHERE * 24778064Sumewalloc(PERSON *pn) 24878064Sume{ 24978064Sume WHERE *w; 25078064Sume 25178064Sume if ((w = malloc(sizeof(WHERE))) == NULL) 25278064Sume err(1, NULL); 25378064Sume if (pn->whead == NULL) 25478064Sume pn->whead = pn->wtail = w; 25578064Sume else { 25678064Sume pn->wtail->next = w; 25778064Sume pn->wtail = w; 25878064Sume } 25978064Sume w->next = NULL; 26078064Sume return(w); 26178064Sume} 262122679Sume 26378064Sumechar * 26478064Sumeprphone(char *num) 26578064Sume{ 26678064Sume char *p; 26778064Sume int len; 26878064Sume static char pbuf[20]; 26978064Sume 27078064Sume /* don't touch anything if the user has their own formatting */ 27178064Sume for (p = num; *p; ++p) 27278064Sume if (!isdigit(*p)) 27378064Sume return(num); 27478064Sume len = p - num; 27578064Sume p = pbuf; 27678064Sume switch(len) { 27778064Sume case 11: /* +0-123-456-7890 */ 278122679Sume *p++ = '+'; 27978064Sume *p++ = *num++; 28078064Sume *p++ = '-'; 28178064Sume /* FALLTHROUGH */ 28278064Sume case 10: /* 012-345-6789 */ 28378064Sume *p++ = *num++; 28478064Sume *p++ = *num++; 28578064Sume *p++ = *num++; 28678064Sume *p++ = '-'; 28778064Sume /* FALLTHROUGH */ 28878064Sume case 7: /* 012-3456 */ 28978064Sume *p++ = *num++; 29078064Sume *p++ = *num++; 29178064Sume *p++ = *num++; 29278064Sume break; 29378064Sume case 5: /* x0-1234 */ 294122679Sume case 4: /* x1234 */ 29578064Sume *p++ = 'x'; 29678064Sume *p++ = *num++; 29778064Sume break; 29878064Sume default: 29978064Sume return(num); 30078064Sume } 30178064Sume if (len != 4) { 30278064Sume *p++ = '-'; 30378064Sume *p++ = *num++; 30478064Sume } 30578064Sume *p++ = *num++; 30678064Sume *p++ = *num++; 30778064Sume *p++ = *num++; 30878064Sume *p = '\0'; 30978064Sume return(pbuf); 31078064Sume} 31178064Sume 31278064Sumestatic void 31378064Sumefind_idle_and_ttywrite(WHERE *w) 31478064Sume{ 31578064Sume struct stat sb; 31678064Sume time_t touched; 31778064Sume int error; 31878064Sume 31978064Sume (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty); 32078064Sume 32178064Sume error = stat(tbuf, &sb); 32278064Sume if (error < 0 && errno == ENOENT) { 323122679Sume /* 32478064Sume * The terminal listed is not actually a terminal (i.e., 32578064Sume * ":0"). This is a failure, so we'll skip printing 32678064Sume * out the idle time, which is non-ideal but better 32778064Sume * than a bogus warning and idle time. 32878064Sume */ 32978064Sume w->idletime = -1; 33078064Sume return; 33178064Sume } else if (error < 0) { 33278064Sume warn("%s", tbuf); 33378064Sume w->idletime = -1; 33478064Sume return; 33578064Sume } 33678064Sume touched = sb.st_atime; 33778064Sume if (touched < w->loginat) { 33878064Sume /* tty untouched since before login */ 33978064Sume touched = w->loginat; 34078064Sume } 34178064Sume w->idletime = now < touched ? 0 : now - touched; 34278064Sume 34378064Sume#define TALKABLE 0220 /* tty is writable if 220 mode */ 34478064Sume w->writable = ((sb.st_mode & TALKABLE) == TALKABLE); 34578064Sume} 346 347static void 348userinfo(PERSON *pn, struct passwd *pw) 349{ 350 char *p, *t; 351 char *bp, name[1024]; 352 struct stat sb; 353 354 pn->realname = pn->office = pn->officephone = pn->homephone = NULL; 355 356 pn->uid = pw->pw_uid; 357 if ((pn->name = strdup(pw->pw_name)) == NULL) 358 err(1, "strdup failed"); 359 if ((pn->dir = strdup(pw->pw_dir)) == NULL) 360 err(1, "strdup failed"); 361 if ((pn->shell = strdup(pw->pw_shell)) == NULL) 362 err(1, "strdup failed"); 363 364 /* why do we skip asterisks!?!? */ 365 (void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf)); 366 tbuf[sizeof(tbuf) - 1] = '\0'; 367 if (*bp == '*') 368 ++bp; 369 370 /* ampersands get replaced by the login name */ 371 if (!(p = strsep(&bp, ","))) 372 return; 373 for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 374 if (*t == '&') { 375 (void)strncpy(t, pw->pw_name, 376 sizeof(name) - (t - name)); 377 name[sizeof(name) - 1] = '\0'; 378 if (islower(*t)) 379 *t = toupper(*t); 380 while (t < &name[sizeof(name) - 1] && *++t) 381 continue; 382 } else { 383 ++t; 384 } 385 } 386 *t = '\0'; 387 if ((pn->realname = strdup(name)) == NULL) 388 err(1, "strdup failed"); 389 pn->office = ((p = strsep(&bp, ",")) && *p) ? 390 strdup(p) : NULL; 391 pn->officephone = ((p = strsep(&bp, ",")) && *p) ? 392 strdup(p) : NULL; 393 pn->homephone = ((p = strsep(&bp, ",")) && *p) ? 394 strdup(p) : NULL; 395 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name); 396 pn->mailrecv = -1; /* -1 == not_valid */ 397 if (stat(tbuf, &sb) < 0) { 398 if (errno != ENOENT) { 399 warn("%s", tbuf); 400 return; 401 } 402 } else if (sb.st_size != 0) { 403 pn->mailrecv = sb.st_mtime; 404 pn->mailread = sb.st_atime; 405 } 406} 407 408/* 409 * Is this user hiding from finger? 410 * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide). 411 */ 412 413int 414hide(struct passwd *pw) 415{ 416 struct stat st; 417 char buf[MAXPATHLEN]; 418 419 if (!pw->pw_dir) 420 return 0; 421 422 snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, _PATH_NOFINGER); 423 424 if (stat(buf, &st) == 0) 425 return 1; 426 427 return 0; 428} 429