finger.c revision 220971
10SN/A/*
2157SN/A * Copyright (c) 1989, 1993
30SN/A *	The Regents of the University of California.  All rights reserved.
40SN/A *
50SN/A * This code is derived from software contributed to Berkeley by
60SN/A * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
7157SN/A *
80SN/A * Redistribution and use in source and binary forms, with or without
9157SN/A * modification, are permitted provided that the following conditions
100SN/A * are met:
110SN/A * 1. Redistributions of source code must retain the above copyright
120SN/A *    notice, this list of conditions and the following disclaimer.
130SN/A * 2. Redistributions in binary form must reproduce the above copyright
140SN/A *    notice, this list of conditions and the following disclaimer in the
150SN/A *    documentation and/or other materials provided with the distribution.
160SN/A * 4. Neither the name of the University nor the names of its contributors
170SN/A *    may be used to endorse or promote products derived from this software
180SN/A *    without specific prior written permission.
190SN/A *
200SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21157SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22157SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23157SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
240SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
250SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
260SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
270SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
280SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
290SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
300SN/A * SUCH DAMAGE.
310SN/A */
320SN/A
330SN/A/*
340SN/A * Luke Mewburn <lm@rmit.edu.au> added the following on 940622:
350SN/A *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
360SN/A *	Unread since ...".)
370SN/A *    - 4 digit phone extensions (3210 is printed as x3210.)
380SN/A *    - host/office toggling in short format with -h & -o.
390SN/A *    - short day names (`Tue' printed instead of `Jun 21' if the
400SN/A *	login time is < 6 days.
410SN/A */
420SN/A
430SN/A#ifndef lint
440SN/Astatic const char copyright[] =
450SN/A"@(#) Copyright (c) 1989, 1993\n\
460SN/A	The Regents of the University of California.  All rights reserved.\n";
470SN/A#endif /* not lint */
480SN/A
490SN/A#if 0
500SN/A#ifndef lint
510SN/Astatic char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
520SN/A#endif
530SN/A#endif
540SN/A
550SN/A#include <sys/cdefs.h>
560SN/A__FBSDID("$FreeBSD: head/usr.bin/finger/finger.c 220971 2011-04-23 14:19:26Z simon $");
570SN/A
580SN/A/*
590SN/A * Finger prints out information about users.  It is not portable since
600SN/A * certain fields (e.g. the full user name, office, and phone numbers) are
610SN/A * extracted from the gecos field of the passwd file which other UNIXes
620SN/A * may not have or may use for other things.
630SN/A *
640SN/A * There are currently two output formats; the short format is one line
650SN/A * per user and displays login name, tty, login time, real name, idle time,
660SN/A * and either remote host information (default) or office location/phone
670SN/A * number, depending on if -h or -o is used respectively.
680SN/A * The long format gives the same information (in a more legible format) as
690SN/A * well as home directory, shell, mail info, and .plan/.project files.
700SN/A */
710SN/A
720SN/A#include <sys/types.h>
730SN/A#include <sys/socket.h>
740SN/A#include <db.h>
750SN/A#include <err.h>
760SN/A#include <pwd.h>
770SN/A#include <stdio.h>
780SN/A#include <stdlib.h>
790SN/A#include <string.h>
800SN/A#include <time.h>
810SN/A#include <unistd.h>
820SN/A#include <utmpx.h>
830SN/A#include <locale.h>
840SN/A
850SN/A#include "finger.h"
860SN/A#include "pathnames.h"
870SN/A
880SN/ADB *db;
890SN/Atime_t now;
900SN/Aint entries, gflag, kflag, lflag, mflag, pplan, sflag, oflag;
910SN/Asa_family_t family = PF_UNSPEC;
920SN/Aint d_first = -1;
930SN/Achar tbuf[1024];
940SN/Aint invoker_root = 0;
950SN/A
960SN/Astatic void loginlist(void);
970SN/Astatic int option(int, char **);
980SN/Astatic void usage(void);
990SN/Astatic void userlist(int, char **);
1000SN/A
1010SN/Astatic int
1020SN/Aoption(int argc, char **argv)
1030SN/A{
1040SN/A	int ch;
1050SN/A
1060SN/A	optind = 1;		/* reset getopt */
1070SN/A
1080SN/A	while ((ch = getopt(argc, argv, "46gklmpsho")) != -1)
1090SN/A		switch(ch) {
1100SN/A		case '4':
1110SN/A			family = AF_INET;
1120SN/A			break;
1130SN/A		case '6':
1140SN/A			family = AF_INET6;
1150SN/A			break;
1160SN/A		case 'g':
1170SN/A			gflag = 1;
1180SN/A			break;
1190SN/A		case 'k':
1200SN/A			kflag = 1;		/* keep going without utmp */
1210SN/A			break;
1220SN/A		case 'l':
1230SN/A			lflag = 1;		/* long format */
1240SN/A			break;
1250SN/A		case 'm':
1260SN/A			mflag = 1;		/* force exact match of names */
1270SN/A			break;
1280SN/A		case 'p':
1290SN/A			pplan = 1;		/* don't show .plan/.project */
1300SN/A			break;
1310SN/A		case 's':
1320SN/A			sflag = 1;		/* short format */
1330SN/A			break;
1340SN/A		case 'h':
1350SN/A			oflag = 0;		/* remote host info */
1360SN/A			break;
1370SN/A		case 'o':
1380SN/A			oflag = 1;		/* office info */
1390SN/A			break;
1400SN/A		case '?':
1410SN/A		default:
1420SN/A			usage();
1430SN/A		}
1440SN/A
1450SN/A	return optind;
1460SN/A}
1470SN/A
1480SN/Astatic void
1490SN/Ausage(void)
1500SN/A{
1510SN/A	(void)fprintf(stderr,
1520SN/A	    "usage: finger [-46gklmpsho] [user ...] [user@host ...]\n");
1530SN/A	exit(1);
1540SN/A}
1550SN/A
1560SN/Aint
1570SN/Amain(int argc, char **argv)
1580SN/A{
1590SN/A	int envargc, argcnt;
1600SN/A	char *envargv[3];
1610SN/A	struct passwd *pw;
1620SN/A	static char myname[] = "finger";
1630SN/A
1640SN/A	if (getuid() == 0 || geteuid() == 0) {
1650SN/A		invoker_root = 1;
1660SN/A		if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) {
1670SN/A			if (setgid(pw->pw_gid) != 0)
1680SN/A				err(1, "setgid()");
1690SN/A			if (setuid(pw->pw_uid) != 0)
1700SN/A				err(1, "setuid()");
1710SN/A		} else {
1720SN/A			if (setgid(UNPRIV_UGID) != 0)
1730SN/A				err(1, "setgid()");
1740SN/A			if (setuid(UNPRIV_UGID) != 0)
1750SN/A				err(1, "setuid()");
1760SN/A		}
1770SN/A	}
1780SN/A
1790SN/A	(void) setlocale(LC_ALL, "");
1800SN/A
1810SN/A				/* remove this line to get remote host */
1820SN/A	oflag = 1;		/* default to old "office" behavior */
1830SN/A
1840SN/A	/*
1850SN/A	 * Process environment variables followed by command line arguments.
1860SN/A	 */
1870SN/A	if ((envargv[1] = getenv("FINGER"))) {
1880SN/A		envargc = 2;
1890SN/A		envargv[0] = myname;
1900SN/A		envargv[2] = NULL;
1910SN/A		(void) option(envargc, envargv);
1920SN/A	}
1930SN/A
1940SN/A	argcnt = option(argc, argv);
1950SN/A	argc -= argcnt;
1960SN/A	argv += argcnt;
1970SN/A
1980SN/A	(void)time(&now);
1990SN/A	setpassent(1);
2000SN/A	if (!*argv) {
2010SN/A		/*
2020SN/A		 * Assign explicit "small" format if no names given and -l
2030SN/A		 * not selected.  Force the -s BEFORE we get names so proper
2040SN/A		 * screening will be done.
2050SN/A		 */
2060SN/A		if (!lflag)
2070SN/A			sflag = 1;	/* if -l not explicit, force -s */
2080SN/A		loginlist();
2090SN/A		if (entries == 0)
2100SN/A			(void)printf("No one logged on.\n");
2110SN/A	} else {
2120SN/A		userlist(argc, argv);
2130SN/A		/*
2140SN/A		 * Assign explicit "large" format if names given and -s not
2150SN/A		 * explicitly stated.  Force the -l AFTER we get names so any
2160SN/A		 * remote finger attempts specified won't be mishandled.
2170SN/A		 */
2180SN/A		if (!sflag)
2190SN/A			lflag = 1;	/* if -s not explicit, force -l */
2200SN/A	}
2210SN/A	if (entries) {
2220SN/A		if (lflag)
2230SN/A			lflag_print();
2240SN/A		else
2250SN/A			sflag_print();
2260SN/A	}
2270SN/A	return (0);
2280SN/A}
2290SN/A
2300SN/Astatic void
2310SN/Aloginlist(void)
2320SN/A{
2330SN/A	PERSON *pn;
2340SN/A	DBT data, key;
2350SN/A	struct passwd *pw;
2360SN/A	struct utmpx *user;
2370SN/A	int r, sflag1;
2380SN/A
2390SN/A	if (kflag)
2400SN/A		errx(1, "can't list logins without reading utmp");
2410SN/A
2420SN/A	setutxent();
2430SN/A	while ((user = getutxent()) != NULL) {
2440SN/A		if (user->ut_type != USER_PROCESS)
2450SN/A			continue;
2460SN/A		if ((pn = find_person(user->ut_user)) == NULL) {
2470SN/A			if ((pw = getpwnam(user->ut_user)) == NULL)
2480SN/A				continue;
2490SN/A			if (hide(pw))
2500SN/A				continue;
2510SN/A			pn = enter_person(pw);
2520SN/A		}
2530SN/A		enter_where(user, pn);
2540SN/A	}
2550SN/A	endutxent();
2560SN/A	if (db && lflag)
2570SN/A		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
2580SN/A			PERSON *tmp;
2590SN/A
2600SN/A			r = (*db->seq)(db, &key, &data, sflag1);
2610SN/A			if (r == -1)
2620SN/A				err(1, "db seq");
2630SN/A			if (r == 1)
2640SN/A				break;
2650SN/A			memmove(&tmp, data.data, sizeof tmp);
2660SN/A			enter_lastlog(tmp);
2670SN/A		}
2680SN/A}
2690SN/A
2700SN/Astatic void
2710SN/Auserlist(int argc, char **argv)
2720SN/A{
2730SN/A	PERSON *pn;
2740SN/A	DBT data, key;
2750SN/A	struct utmpx *user;
2760SN/A	struct passwd *pw;
2770SN/A	int r, sflag1, *used, *ip;
2780SN/A	char **ap, **nargv, **np, **p;
2790SN/A	FILE *conf_fp;
2800SN/A	char conf_alias[LINE_MAX];
2810SN/A	char *conf_realname;
2820SN/A	int conf_length;
2830SN/A
2840SN/A	if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
2850SN/A	    (used = calloc(argc, sizeof(int))) == NULL)
2860SN/A		err(1, NULL);
2870SN/A
2880SN/A	/* Pull out all network requests. */
289	for (ap = p = argv, np = nargv; *p; ++p)
290		if (index(*p, '@'))
291			*np++ = *p;
292		else
293			*ap++ = *p;
294
295	*np++ = NULL;
296	*ap++ = NULL;
297
298	if (!*argv)
299		goto net;
300
301	/*
302	 * Mark any arguments beginning with '/' as invalid so that we
303	 * don't accidently confuse them with expansions from finger.conf
304	 */
305	for (p = argv, ip = used; *p; ++p, ++ip)
306	    if (**p == '/') {
307		*ip = 1;
308		warnx("%s: no such user", *p);
309	    }
310
311	/*
312	 * Traverse the finger alias configuration file of the form
313	 * alias:(user|alias), ignoring comment lines beginning '#'.
314	 */
315	if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) {
316	    while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) {
317		conf_length = strlen(conf_alias);
318		if (*conf_alias == '#' || conf_alias[--conf_length] != '\n')
319		    continue;
320		conf_alias[conf_length] = '\0';      /* Remove trailing LF */
321		if ((conf_realname = strchr(conf_alias, ':')) == NULL)
322		    continue;
323		*conf_realname = '\0';               /* Replace : with NUL */
324		for (p = argv; *p; ++p) {
325		    if (strcmp(*p, conf_alias) == 0) {
326			if ((*p = strdup(conf_realname+1)) == NULL) {
327			    err(1, NULL);
328			}
329		    }
330		}
331	    }
332	    (void)fclose(conf_fp);
333	}
334
335	/*
336	 * Traverse the list of possible login names and check the login name
337	 * and real name against the name specified by the user. If the name
338	 * begins with a '/', try to read the file of that name instead of
339	 * gathering the traditional finger information.
340	 */
341	if (mflag)
342		for (p = argv, ip = used; *p; ++p, ++ip) {
343			if (**p != '/' || *ip == 1 || !show_text("", *p, "")) {
344				if (((pw = getpwnam(*p)) != NULL) && !hide(pw))
345					enter_person(pw);
346				else if (!*ip)
347					warnx("%s: no such user", *p);
348			}
349		}
350	else {
351		while ((pw = getpwent()) != NULL) {
352			for (p = argv, ip = used; *p; ++p, ++ip)
353				if (**p == '/' && *ip != 1
354				    && show_text("", *p, ""))
355					*ip = 1;
356				else if (match(pw, *p) && !hide(pw)) {
357					enter_person(pw);
358					*ip = 1;
359				}
360		}
361		for (p = argv, ip = used; *p; ++p, ++ip)
362			if (!*ip)
363				warnx("%s: no such user", *p);
364	}
365
366	/* Handle network requests. */
367net:	for (p = nargv; *p;) {
368		netfinger(*p++);
369		if (*p || entries)
370		    printf("\n");
371	}
372
373	free(used);
374	if (entries == 0)
375		return;
376
377	if (kflag)
378		return;
379
380	/*
381	 * Scan thru the list of users currently logged in, saving
382	 * appropriate data whenever a match occurs.
383	 */
384	setutxent();
385	while ((user = getutxent()) != NULL) {
386		if (user->ut_type != USER_PROCESS)
387			continue;
388		if ((pn = find_person(user->ut_user)) == NULL)
389			continue;
390		enter_where(user, pn);
391	}
392	endutxent();
393	if (db)
394		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
395			PERSON *tmp;
396
397			r = (*db->seq)(db, &key, &data, sflag1);
398			if (r == -1)
399				err(1, "db seq");
400			if (r == 1)
401				break;
402			memmove(&tmp, data.data, sizeof tmp);
403			enter_lastlog(tmp);
404		}
405}
406