11590Srgrimes/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 4. Neither the name of the University nor the names of its contributors
171590Srgrimes *    may be used to endorse or promote products derived from this software
181590Srgrimes *    without specific prior written permission.
191590Srgrimes *
201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301590Srgrimes * SUCH DAMAGE.
311590Srgrimes */
321590Srgrimes
332537Spst/*
342537Spst * Luke Mewburn <lm@rmit.edu.au> added the following on 940622:
352537Spst *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
362537Spst *	Unread since ...".)
372537Spst *    - 4 digit phone extensions (3210 is printed as x3210.)
382537Spst *    - host/office toggling in short format with -h & -o.
392537Spst *    - short day names (`Tue' printed instead of `Jun 21' if the
402537Spst *	login time is < 6 days.
412537Spst */
422537Spst
431590Srgrimes#ifndef lint
4467467Srustatic const char copyright[] =
451590Srgrimes"@(#) Copyright (c) 1989, 1993\n\
461590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
471590Srgrimes#endif /* not lint */
481590Srgrimes
4987628Sdwmalone#if 0
501590Srgrimes#ifndef lint
5187628Sdwmalonestatic char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
5272109Scharnier#endif
5387628Sdwmalone#endif
541590Srgrimes
5587628Sdwmalone#include <sys/cdefs.h>
5687628Sdwmalone__FBSDID("$FreeBSD$");
5787628Sdwmalone
581590Srgrimes/*
591590Srgrimes * Finger prints out information about users.  It is not portable since
601590Srgrimes * certain fields (e.g. the full user name, office, and phone numbers) are
611590Srgrimes * extracted from the gecos field of the passwd file which other UNIXes
621590Srgrimes * may not have or may use for other things.
631590Srgrimes *
641590Srgrimes * There are currently two output formats; the short format is one line
651590Srgrimes * per user and displays login name, tty, login time, real name, idle time,
662537Spst * and either remote host information (default) or office location/phone
672537Spst * number, depending on if -h or -o is used respectively.
682537Spst * The long format gives the same information (in a more legible format) as
692537Spst * well as home directory, shell, mail info, and .plan/.project files.
701590Srgrimes */
711590Srgrimes
72100521Sume#include <sys/types.h>
73100521Sume#include <sys/socket.h>
7423693Speter#include <db.h>
7523693Speter#include <err.h>
761590Srgrimes#include <pwd.h>
771590Srgrimes#include <stdio.h>
781590Srgrimes#include <stdlib.h>
791590Srgrimes#include <string.h>
8023693Speter#include <time.h>
8123693Speter#include <unistd.h>
82202191Sed#include <utmpx.h>
8311759Sache#include <locale.h>
8423693Speter
851590Srgrimes#include "finger.h"
8664775Sbrian#include "pathnames.h"
871590Srgrimes
881590SrgrimesDB *db;
891590Srgrimestime_t now;
90241737Sedstatic int kflag, mflag, sflag;
91241737Sedint entries, gflag, lflag, pplan, oflag;
92106250Ssobomaxsa_family_t family = PF_UNSPEC;
9374586Sacheint d_first = -1;
941590Srgrimeschar tbuf[1024];
95150316Sddsint invoker_root = 0;
961590Srgrimes
9792920Simpstatic void loginlist(void);
9892920Simpstatic int option(int, char **);
9992920Simpstatic void usage(void);
10092920Simpstatic void userlist(int, char **);
1011590Srgrimes
10287229Smarkmstatic int
103102944Sdwmaloneoption(int argc, char **argv)
1041590Srgrimes{
1051590Srgrimes	int ch;
1061590Srgrimes
1072589Spst	optind = 1;		/* reset getopt */
1082537Spst
109168635Sdes	while ((ch = getopt(argc, argv, "46gklmpsho")) != -1)
1101590Srgrimes		switch(ch) {
111100521Sume		case '4':
112100521Sume			family = AF_INET;
113100521Sume			break;
114100521Sume		case '6':
115100521Sume			family = AF_INET6;
116100521Sume			break;
11799249Smini		case 'g':
11899249Smini			gflag = 1;
11999249Smini			break;
120117010Sjmallett		case 'k':
121117010Sjmallett			kflag = 1;		/* keep going without utmp */
122117010Sjmallett			break;
1231590Srgrimes		case 'l':
1241590Srgrimes			lflag = 1;		/* long format */
1251590Srgrimes			break;
1261590Srgrimes		case 'm':
1271590Srgrimes			mflag = 1;		/* force exact match of names */
1281590Srgrimes			break;
1291590Srgrimes		case 'p':
1301590Srgrimes			pplan = 1;		/* don't show .plan/.project */
1311590Srgrimes			break;
1321590Srgrimes		case 's':
1331590Srgrimes			sflag = 1;		/* short format */
1341590Srgrimes			break;
1352537Spst		case 'h':
1362537Spst			oflag = 0;		/* remote host info */
1372537Spst			break;
1382537Spst		case 'o':
1392537Spst			oflag = 1;		/* office info */
1402537Spst			break;
1411590Srgrimes		case '?':
1421590Srgrimes		default:
14327169Scharnier			usage();
1441590Srgrimes		}
1451590Srgrimes
1462589Spst	return optind;
1472589Spst}
1482589Spst
14927169Scharnierstatic void
150102944Sdwmaloneusage(void)
15127169Scharnier{
152146466Sru	(void)fprintf(stderr,
153168635Sdes	    "usage: finger [-46gklmpsho] [user ...] [user@host ...]\n");
15427169Scharnier	exit(1);
15527169Scharnier}
15627169Scharnier
15727169Scharnierint
158102944Sdwmalonemain(int argc, char **argv)
1592589Spst{
16027169Scharnier	int envargc, argcnt;
1612589Spst	char *envargv[3];
16246662Sobrien	struct passwd *pw;
16387229Smarkm	static char myname[] = "finger";
1642589Spst
16546662Sobrien	if (getuid() == 0 || geteuid() == 0) {
166150316Sdds		invoker_root = 1;
16746662Sobrien		if ((pw = getpwnam(UNPRIV_NAME)) && pw->pw_uid > 0) {
168220971Ssimon			if (setgid(pw->pw_gid) != 0)
169220971Ssimon				err(1, "setgid()");
170220971Ssimon			if (setuid(pw->pw_uid) != 0)
171220971Ssimon				err(1, "setuid()");
17246662Sobrien		} else {
173220971Ssimon			if (setgid(UNPRIV_UGID) != 0)
174220971Ssimon				err(1, "setgid()");
175220971Ssimon			if (setuid(UNPRIV_UGID) != 0)
176220971Ssimon				err(1, "setuid()");
17746662Sobrien		}
17846662Sobrien	}
17946662Sobrien
18011811Sache	(void) setlocale(LC_ALL, "");
18111759Sache
1822589Spst				/* remove this line to get remote host */
1832589Spst	oflag = 1;		/* default to old "office" behavior */
1842589Spst
1852589Spst	/*
1862589Spst	 * Process environment variables followed by command line arguments.
1872589Spst	 */
1882589Spst	if ((envargv[1] = getenv("FINGER"))) {
1892589Spst		envargc = 2;
19087229Smarkm		envargv[0] = myname;
1912589Spst		envargv[2] = NULL;
1922589Spst		(void) option(envargc, envargv);
1932589Spst	}
1942589Spst
1952589Spst	argcnt = option(argc, argv);
1962589Spst	argc -= argcnt;
1972589Spst	argv += argcnt;
1982589Spst
1991590Srgrimes	(void)time(&now);
2001590Srgrimes	setpassent(1);
2011590Srgrimes	if (!*argv) {
2021590Srgrimes		/*
2031590Srgrimes		 * Assign explicit "small" format if no names given and -l
2041590Srgrimes		 * not selected.  Force the -s BEFORE we get names so proper
2051590Srgrimes		 * screening will be done.
2061590Srgrimes		 */
2071590Srgrimes		if (!lflag)
2081590Srgrimes			sflag = 1;	/* if -l not explicit, force -s */
2091590Srgrimes		loginlist();
2101590Srgrimes		if (entries == 0)
2111590Srgrimes			(void)printf("No one logged on.\n");
2121590Srgrimes	} else {
2131590Srgrimes		userlist(argc, argv);
2141590Srgrimes		/*
2151590Srgrimes		 * Assign explicit "large" format if names given and -s not
2161590Srgrimes		 * explicitly stated.  Force the -l AFTER we get names so any
2171590Srgrimes		 * remote finger attempts specified won't be mishandled.
2181590Srgrimes		 */
2191590Srgrimes		if (!sflag)
2201590Srgrimes			lflag = 1;	/* if -s not explicit, force -l */
2211590Srgrimes	}
22248566Sbillf	if (entries) {
2231590Srgrimes		if (lflag)
2241590Srgrimes			lflag_print();
2251590Srgrimes		else
2261590Srgrimes			sflag_print();
22748566Sbillf	}
22823693Speter	return (0);
2291590Srgrimes}
2301590Srgrimes
2311590Srgrimesstatic void
232102944Sdwmaloneloginlist(void)
2331590Srgrimes{
23487229Smarkm	PERSON *pn;
2351590Srgrimes	DBT data, key;
2361590Srgrimes	struct passwd *pw;
237201140Sed	struct utmpx *user;
23887229Smarkm	int r, sflag1;
2391590Srgrimes
240117010Sjmallett	if (kflag)
241117010Sjmallett		errx(1, "can't list logins without reading utmp");
242117010Sjmallett
243201140Sed	setutxent();
244201140Sed	while ((user = getutxent()) != NULL) {
245201140Sed		if (user->ut_type != USER_PROCESS)
2461590Srgrimes			continue;
247201140Sed		if ((pn = find_person(user->ut_user)) == NULL) {
248201140Sed			if ((pw = getpwnam(user->ut_user)) == NULL)
2491590Srgrimes				continue;
2505369Sjkh			if (hide(pw))
2515369Sjkh				continue;
2521590Srgrimes			pn = enter_person(pw);
2531590Srgrimes		}
254201140Sed		enter_where(user, pn);
2551590Srgrimes	}
256201140Sed	endutxent();
2571590Srgrimes	if (db && lflag)
25887229Smarkm		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
25923693Speter			PERSON *tmp;
26023693Speter
26187229Smarkm			r = (*db->seq)(db, &key, &data, sflag1);
2621590Srgrimes			if (r == -1)
26323693Speter				err(1, "db seq");
2641590Srgrimes			if (r == 1)
2651590Srgrimes				break;
26623693Speter			memmove(&tmp, data.data, sizeof tmp);
26723693Speter			enter_lastlog(tmp);
2681590Srgrimes		}
2691590Srgrimes}
2701590Srgrimes
2711590Srgrimesstatic void
272102944Sdwmaloneuserlist(int argc, char **argv)
2731590Srgrimes{
27487229Smarkm	PERSON *pn;
2751590Srgrimes	DBT data, key;
276201140Sed	struct utmpx *user;
2771590Srgrimes	struct passwd *pw;
27887229Smarkm	int r, sflag1, *used, *ip;
2791590Srgrimes	char **ap, **nargv, **np, **p;
28064775Sbrian	FILE *conf_fp;
28164775Sbrian	char conf_alias[LINE_MAX];
28264775Sbrian	char *conf_realname;
28364775Sbrian	int conf_length;
2841590Srgrimes
2851590Srgrimes	if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
2861590Srgrimes	    (used = calloc(argc, sizeof(int))) == NULL)
28723693Speter		err(1, NULL);
2881590Srgrimes
2891590Srgrimes	/* Pull out all network requests. */
2901590Srgrimes	for (ap = p = argv, np = nargv; *p; ++p)
291229403Sed		if (strchr(*p, '@'))
2921590Srgrimes			*np++ = *p;
2931590Srgrimes		else
2941590Srgrimes			*ap++ = *p;
2951590Srgrimes
2961590Srgrimes	*np++ = NULL;
2971590Srgrimes	*ap++ = NULL;
2981590Srgrimes
2991590Srgrimes	if (!*argv)
3001590Srgrimes		goto net;
3011590Srgrimes
3021590Srgrimes	/*
303228992Suqs	 * Mark any arguments beginning with '/' as invalid so that we
304228992Suqs	 * don't accidentally confuse them with expansions from finger.conf
30566563Sbrian	 */
30666563Sbrian	for (p = argv, ip = used; *p; ++p, ++ip)
30766563Sbrian	    if (**p == '/') {
30866563Sbrian		*ip = 1;
30966563Sbrian		warnx("%s: no such user", *p);
31066563Sbrian	    }
31166563Sbrian
31266563Sbrian	/*
31364775Sbrian	 * Traverse the finger alias configuration file of the form
31464775Sbrian	 * alias:(user|alias), ignoring comment lines beginning '#'.
31564775Sbrian	 */
31664775Sbrian	if ((conf_fp = fopen(_PATH_FINGERCONF, "r")) != NULL) {
31764775Sbrian	    while(fgets(conf_alias, sizeof(conf_alias), conf_fp) != NULL) {
31864775Sbrian		conf_length = strlen(conf_alias);
31964775Sbrian		if (*conf_alias == '#' || conf_alias[--conf_length] != '\n')
32064775Sbrian		    continue;
32164775Sbrian		conf_alias[conf_length] = '\0';      /* Remove trailing LF */
32264775Sbrian		if ((conf_realname = strchr(conf_alias, ':')) == NULL)
32364775Sbrian		    continue;
32464775Sbrian		*conf_realname = '\0';               /* Replace : with NUL */
32564775Sbrian		for (p = argv; *p; ++p) {
326126960Sbde		    if (strcmp(*p, conf_alias) == 0) {
32764775Sbrian			if ((*p = strdup(conf_realname+1)) == NULL) {
32864775Sbrian			    err(1, NULL);
32964775Sbrian			}
33064775Sbrian		    }
33164775Sbrian		}
33264775Sbrian	    }
33364775Sbrian	    (void)fclose(conf_fp);
33464775Sbrian	}
33564775Sbrian
33664775Sbrian	/*
3371590Srgrimes	 * Traverse the list of possible login names and check the login name
33865064Sbrian	 * and real name against the name specified by the user. If the name
33965064Sbrian	 * begins with a '/', try to read the file of that name instead of
34065064Sbrian	 * gathering the traditional finger information.
3411590Srgrimes	 */
3421590Srgrimes	if (mflag)
34366675Sru		for (p = argv, ip = used; *p; ++p, ++ip) {
34466675Sru			if (**p != '/' || *ip == 1 || !show_text("", *p, "")) {
34565064Sbrian				if (((pw = getpwnam(*p)) != NULL) && !hide(pw))
34665064Sbrian					enter_person(pw);
34766675Sru				else if (!*ip)
34865064Sbrian					warnx("%s: no such user", *p);
34965064Sbrian			}
35065064Sbrian		}
3511590Srgrimes	else {
35265787Sbrian		while ((pw = getpwent()) != NULL) {
3531590Srgrimes			for (p = argv, ip = used; *p; ++p, ++ip)
35465064Sbrian				if (**p == '/' && *ip != 1
35565787Sbrian				    && show_text("", *p, ""))
35665064Sbrian					*ip = 1;
35765787Sbrian				else if (match(pw, *p) && !hide(pw)) {
3581590Srgrimes					enter_person(pw);
3591590Srgrimes					*ip = 1;
3601590Srgrimes				}
3615369Sjkh		}
3621590Srgrimes		for (p = argv, ip = used; *p; ++p, ++ip)
3631590Srgrimes			if (!*ip)
36427169Scharnier				warnx("%s: no such user", *p);
3651590Srgrimes	}
3661590Srgrimes
3671590Srgrimes	/* Handle network requests. */
3684991Spstnet:	for (p = nargv; *p;) {
3691590Srgrimes		netfinger(*p++);
3704991Spst		if (*p || entries)
3714991Spst		    printf("\n");
3724991Spst	}
3731590Srgrimes
374200793Sdelphij	free(used);
3751590Srgrimes	if (entries == 0)
3761590Srgrimes		return;
3771590Srgrimes
378117010Sjmallett	if (kflag)
379117010Sjmallett		return;
380117010Sjmallett
3811590Srgrimes	/*
3821590Srgrimes	 * Scan thru the list of users currently logged in, saving
3831590Srgrimes	 * appropriate data whenever a match occurs.
3841590Srgrimes	 */
385201140Sed	setutxent();
386201140Sed	while ((user = getutxent()) != NULL) {
387201140Sed		if (user->ut_type != USER_PROCESS)
3881590Srgrimes			continue;
389201140Sed		if ((pn = find_person(user->ut_user)) == NULL)
3901590Srgrimes			continue;
391201140Sed		enter_where(user, pn);
3921590Srgrimes	}
393201140Sed	endutxent();
3941590Srgrimes	if (db)
39587229Smarkm		for (sflag1 = R_FIRST;; sflag1 = R_NEXT) {
39623693Speter			PERSON *tmp;
39723693Speter
39887229Smarkm			r = (*db->seq)(db, &key, &data, sflag1);
3991590Srgrimes			if (r == -1)
40023693Speter				err(1, "db seq");
4011590Srgrimes			if (r == 1)
4021590Srgrimes				break;
40323693Speter			memmove(&tmp, data.data, sizeof tmp);
40423693Speter			enter_lastlog(tmp);
4051590Srgrimes		}
4061590Srgrimes}
407