util.c revision 201140
1104862Sru/* 2104862Sru * Copyright (c) 1989, 1993 375584Sru * The Regents of the University of California. All rights reserved. 475584Sru * 575584Sru * This code is derived from software contributed to Berkeley by 675584Sru * Tony Nardo of the Johns Hopkins University/Applied Physics Lab. 775584Sru * 875584Sru * Redistribution and use in source and binary forms, with or without 975584Sru * modification, are permitted provided that the following conditions 1075584Sru * are met: 1175584Sru * 1. Redistributions of source code must retain the above copyright 1275584Sru * notice, this list of conditions and the following disclaimer. 1375584Sru * 2. Redistributions in binary form must reproduce the above copyright 1475584Sru * notice, this list of conditions and the following disclaimer in the 1575584Sru * documentation and/or other materials provided with the distribution. 1675584Sru * 3. All advertising materials mentioning features or use of this software 1775584Sru * must display the following acknowledgement: 1875584Sru * This product includes software developed by the University of 19151497Sru * California, Berkeley and its contributors. 2075584Sru * 4. Neither the name of the University nor the names of its contributors 21104862Sru * may be used to endorse or promote products derived from this software 22104862Sru * without specific prior written permission. 23104862Sru * 24104862Sru * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2575584Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2675584Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2775584Sru * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2875584Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2975584Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3075584Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3175584Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3275584Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3375584Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3475584Sru * SUCH DAMAGE. 3575584Sru */ 3675584Sru 3775584Sru#if 0 3875584Sru#ifndef lint 3975584Srustatic char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/28/95"; 4075584Sru#endif 4175584Sru#endif 4275584Sru 4375584Sru#include <sys/cdefs.h> 4475584Sru__FBSDID("$FreeBSD: head/usr.bin/finger/util.c 201140 2009-12-28 20:54:34Z ed $"); 4575584Sru 4675584Sru#include <sys/param.h> 4775584Sru#include <sys/socket.h> 4875584Sru#include <sys/stat.h> 4975584Sru#include <ctype.h> 5075584Sru#include <db.h> 5175584Sru#include <err.h> 5275584Sru#include <errno.h> 5375584Sru#include <fcntl.h> 5475584Sru#include <paths.h> 5575584Sru#include <pwd.h> 5675584Sru#include <stdio.h> 5775584Sru#include <stdlib.h> 5875584Sru#include <string.h> 5975584Sru#define _ULOG_POSIX_NAMES 6075584Sru#include <ulog.h> 6175584Sru#include <unistd.h> 6275584Sru#include "finger.h" 6375584Sru#include "pathnames.h" 6475584Sru 6575584Srustatic void find_idle_and_ttywrite(WHERE *); 6675584Srustatic void userinfo(PERSON *, struct passwd *); 6775584Srustatic WHERE *walloc(PERSON *); 6875584Sru 6975584Sruint 7075584Srumatch(struct passwd *pw, const char *user) 7175584Sru{ 7275584Sru char *p, *t; 7375584Sru char name[1024]; 7475584Sru 7575584Sru if (!strcasecmp(pw->pw_name, user)) 7675584Sru return(1); 7775584Sru 7875584Sru /* 7975584Sru * XXX 8075584Sru * Why do we skip asterisks!?!? 8175584Sru */ 8275584Sru (void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf)); 8375584Sru tbuf[sizeof(tbuf) - 1] = '\0'; 8475584Sru if (*p == '*') 8575584Sru ++p; 8675584Sru 8775584Sru /* Ampersands get replaced by the login name. */ 8875584Sru if ((p = strtok(p, ",")) == NULL) 8975584Sru return(0); 9075584Sru 9175584Sru for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) { 9275584Sru if (*t == '&') { 9375584Sru (void)strncpy(t, pw->pw_name, 9475584Sru sizeof(name) - (t - name)); 9575584Sru name[sizeof(name) - 1] = '\0'; 9675584Sru while (t < &name[sizeof(name) - 1] && *++t) 9775584Sru continue; 9875584Sru } else { 9975584Sru ++t; 10075584Sru } 10175584Sru } 10275584Sru *t = '\0'; 10375584Sru for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL) 10475584Sru if (!strcasecmp(p, user)) 10575584Sru return(1); 10675584Sru return(0); 10775584Sru} 10875584Sru 10975584Sruvoid 11075584Sruenter_lastlog(PERSON *pn) 11175584Sru{ 11275584Sru WHERE *w; 11375584Sru struct ulog_utmpx *ut; 11475584Sru char doit = 0; 11575584Sru 11675584Sru ulog_setutxfile(UTXI_USER, NULL); 11775584Sru ut = ulog_getutxuser(pn->name); 11875584Sru if ((w = pn->whead) == NULL) 11975584Sru doit = 1; 12075584Sru else if (ut != NULL && ut->ut_type == USER_PROCESS) { 12175584Sru /* if last login is earlier than some current login */ 12275584Sru for (; !doit && w != NULL; w = w->next) 12375584Sru if (w->info == LOGGEDIN && 12475584Sru w->loginat < ut->ut_tv.tv_sec) 12575584Sru doit = 1; 12675584Sru /* 12775584Sru * and if it's not any of the current logins 12875584Sru * can't use time comparison because there may be a small 12975584Sru * discrepancy since login calls time() twice 13075584Sru */ 13175584Sru for (w = pn->whead; doit && w != NULL; w = w->next) 13275584Sru if (w->info == LOGGEDIN && 13375584Sru 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