1/*
2   Unix SMB/CIFS implementation.
3   session handling for utmp and PAM
4   Copyright (C) tridge@samba.org 2001
5   Copyright (C) abartlet@pcug.org.au 2001
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20*/
21
22/* a "session" is claimed when we do a SessionSetupX operation
23   and is yielded when the corresponding vuid is destroyed.
24
25   sessions are used to populate utmp and PAM session structures
26*/
27
28#include "includes.h"
29
30static TDB_CONTEXT *tdb;
31/* called when a session is created */
32BOOL session_claim(user_struct *vuser)
33{
34	int i = 0;
35	TDB_DATA data;
36	struct sockaddr sa;
37	struct in_addr *client_ip;
38	struct sessionid sessionid;
39	uint32 pid = (uint32)sys_getpid();
40	TDB_DATA key;
41	fstring keystr;
42	char * hostname;
43	int tdb_store_flag;  /* If using utmp, we do an inital 'lock hold' store,
44				but we don't need this if we are just using the
45				(unique) pid/vuid combination */
46
47	vuser->session_keystr = NULL;
48
49	/* don't register sessions for the guest user - its just too
50	   expensive to go through pam session code for browsing etc */
51	if (vuser->guest) {
52		return True;
53	}
54
55	if (!tdb) {
56		tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
57			       O_RDWR | O_CREAT, 0644);
58		if (!tdb) {
59			DEBUG(1,("session_claim: failed to open sessionid tdb\n"));
60			return False;
61		}
62	}
63
64	ZERO_STRUCT(sessionid);
65
66	data.dptr = NULL;
67	data.dsize = 0;
68
69	if (lp_utmp()) {
70		for (i=1;i<MAX_SESSION_ID;i++) {
71			slprintf(keystr, sizeof(keystr)-1, "ID/%d", i);
72			key.dptr = keystr;
73			key.dsize = strlen(keystr)+1;
74
75			if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break;
76		}
77
78		if (i == MAX_SESSION_ID) {
79			DEBUG(1,("session_claim: out of session IDs (max is %d)\n",
80				 MAX_SESSION_ID));
81			return False;
82		}
83		slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_UTMP_TEMPLATE, i);
84		tdb_store_flag = TDB_MODIFY;
85	} else
86	{
87		slprintf(keystr, sizeof(keystr)-1, "ID/%lu/%u",
88			 (long unsigned int)sys_getpid(),
89			 vuser->vuid);
90		slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1,
91			 SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
92			 vuser->vuid);
93
94		key.dptr = keystr;
95		key.dsize = strlen(keystr)+1;
96
97		tdb_store_flag = TDB_REPLACE;
98	}
99
100	/* If 'hostname lookup' == yes, then do the DNS lookup.  This is
101           needed because utmp and PAM both expect DNS names
102
103	   client_name() handles this case internally.
104	*/
105
106	hostname = client_name();
107	if (strcmp(hostname, "UNKNOWN") == 0) {
108		hostname = client_addr();
109	}
110
111	fstrcpy(sessionid.username, vuser->user.unix_name);
112	fstrcpy(sessionid.hostname, hostname);
113	sessionid.id_num = i;  /* Only valid for utmp sessions */
114	sessionid.pid = pid;
115	sessionid.uid = vuser->uid;
116	sessionid.gid = vuser->gid;
117	fstrcpy(sessionid.remote_machine, get_remote_machine_name());
118	fstrcpy(sessionid.ip_addr, client_addr());
119
120	client_ip = client_inaddr(&sa);
121
122	if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) {
123		DEBUG(1,("pam_session rejected the session for %s [%s]\n",
124				sessionid.username, sessionid.id_str));
125		if (tdb_store_flag == TDB_MODIFY) {
126			tdb_delete(tdb, key);
127		}
128		return False;
129	}
130
131	data.dptr = (char *)&sessionid;
132	data.dsize = sizeof(sessionid);
133	if (tdb_store(tdb, key, data, tdb_store_flag) != 0) {
134		DEBUG(1,("session_claim: unable to create session id record\n"));
135		return False;
136	}
137
138	if (lp_utmp()) {
139		sys_utmp_claim(sessionid.username, sessionid.hostname,
140			       client_ip,
141			       sessionid.id_str, sessionid.id_num);
142	}
143
144	vuser->session_keystr = strdup(keystr);
145	if (!vuser->session_keystr) {
146		DEBUG(0, ("session_claim:  strdup() failed for session_keystr\n"));
147		return False;
148	}
149	return True;
150}
151
152/* called when a session is destroyed */
153void session_yield(user_struct *vuser)
154{
155	TDB_DATA dbuf;
156	struct sessionid sessionid;
157	struct in_addr *client_ip;
158	TDB_DATA key;
159
160	if (!tdb) return;
161
162	if (!vuser->session_keystr) {
163		return;
164	}
165
166	key.dptr = vuser->session_keystr;
167	key.dsize = strlen(vuser->session_keystr)+1;
168
169	dbuf = tdb_fetch(tdb, key);
170
171	if (dbuf.dsize != sizeof(sessionid))
172		return;
173
174	memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
175
176	client_ip = interpret_addr2(sessionid.ip_addr);
177
178	SAFE_FREE(dbuf.dptr);
179
180	if (lp_utmp()) {
181		sys_utmp_yield(sessionid.username, sessionid.hostname,
182			       client_ip,
183			       sessionid.id_str, sessionid.id_num);
184	}
185
186	smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname);
187
188	tdb_delete(tdb, key);
189}
190
191static BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state)
192{
193	if (!tdb) {
194		DEBUG(3, ("No tdb opened\n"));
195		return False;
196	}
197
198	tdb_traverse(tdb, fn, state);
199	return True;
200}
201
202struct session_list {
203	int count;
204	struct sessionid *sessions;
205};
206
207static int gather_sessioninfo(TDB_CONTEXT *stdb, TDB_DATA kbuf, TDB_DATA dbuf,
208			      void *state)
209{
210	struct session_list *sesslist = (struct session_list *) state;
211	const struct sessionid *current = (const struct sessionid *) dbuf.dptr;
212
213	sesslist->count += 1;
214	sesslist->sessions = REALLOC(sesslist->sessions, sesslist->count *
215				      sizeof(struct sessionid));
216
217	memcpy(&sesslist->sessions[sesslist->count - 1], current,
218	       sizeof(struct sessionid));
219	DEBUG(7,("gather_sessioninfo session from %s@%s\n",
220		 current->username, current->remote_machine));
221	return 0;
222}
223
224int list_sessions(struct sessionid **session_list)
225{
226	struct session_list sesslist;
227
228	sesslist.count = 0;
229	sesslist.sessions = NULL;
230
231	if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) {
232		DEBUG(3, ("Session traverse failed\n"));
233		SAFE_FREE(sesslist.sessions);
234		*session_list = NULL;
235		return 0;
236	}
237
238	*session_list = sesslist.sessions;
239	return sesslist.count;
240}
241
242