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
32BOOL session_init(void)
33{
34	if (tdb)
35		return True;
36
37	tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT,
38		       O_RDWR | O_CREAT, 0644);
39	if (!tdb) {
40		DEBUG(1,("session_init: failed to open sessionid tdb\n"));
41		return False;
42	}
43
44	return True;
45}
46
47/* called when a session is created */
48BOOL session_claim(user_struct *vuser)
49{
50	int i = 0;
51	TDB_DATA data;
52	struct sockaddr sa;
53	struct in_addr *client_ip;
54	struct sessionid sessionid;
55	uint32 pid = (uint32)sys_getpid();
56	TDB_DATA key;
57	fstring keystr;
58	char * hostname;
59	int tdb_store_flag;  /* If using utmp, we do an inital 'lock hold' store,
60				but we don't need this if we are just using the
61				(unique) pid/vuid combination */
62
63	vuser->session_keystr = NULL;
64
65	/* don't register sessions for the guest user - its just too
66	   expensive to go through pam session code for browsing etc */
67	if (vuser->guest) {
68		return True;
69	}
70
71	if (!session_init())
72		return False;
73
74	ZERO_STRUCT(sessionid);
75
76	data.dptr = NULL;
77	data.dsize = 0;
78
79	if (lp_utmp()) {
80		for (i=1;i<MAX_SESSION_ID;i++) {
81			slprintf(keystr, sizeof(keystr)-1, "ID/%d", i);
82			key.dptr = keystr;
83			key.dsize = strlen(keystr)+1;
84
85			if (tdb_store(tdb, key, data, TDB_INSERT) == 0) break;
86		}
87
88		if (i == MAX_SESSION_ID) {
89			DEBUG(1,("session_claim: out of session IDs (max is %d)\n",
90				 MAX_SESSION_ID));
91			return False;
92		}
93		slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1, SESSION_UTMP_TEMPLATE, i);
94		tdb_store_flag = TDB_MODIFY;
95	} else
96	{
97		slprintf(keystr, sizeof(keystr)-1, "ID/%lu/%u",
98			 (long unsigned int)sys_getpid(),
99			 vuser->vuid);
100		slprintf(sessionid.id_str, sizeof(sessionid.id_str)-1,
101			 SESSION_TEMPLATE, (long unsigned int)sys_getpid(),
102			 vuser->vuid);
103
104		key.dptr = keystr;
105		key.dsize = strlen(keystr)+1;
106
107		tdb_store_flag = TDB_REPLACE;
108	}
109
110	/* If 'hostname lookup' == yes, then do the DNS lookup.  This is
111           needed because utmp and PAM both expect DNS names
112
113	   client_name() handles this case internally.
114	*/
115
116	hostname = client_name();
117	if (strcmp(hostname, "UNKNOWN") == 0) {
118		hostname = client_addr();
119	}
120
121	fstrcpy(sessionid.username, vuser->user.unix_name);
122	fstrcpy(sessionid.hostname, hostname);
123	sessionid.id_num = i;  /* Only valid for utmp sessions */
124	sessionid.pid = pid;
125	sessionid.uid = vuser->uid;
126	sessionid.gid = vuser->gid;
127	fstrcpy(sessionid.remote_machine, get_remote_machine_name());
128	fstrcpy(sessionid.ip_addr, client_addr());
129
130	client_ip = client_inaddr(&sa);
131
132	if (!smb_pam_claim_session(sessionid.username, sessionid.id_str, sessionid.hostname)) {
133		DEBUG(1,("pam_session rejected the session for %s [%s]\n",
134				sessionid.username, sessionid.id_str));
135		if (tdb_store_flag == TDB_MODIFY) {
136			tdb_delete(tdb, key);
137		}
138		return False;
139	}
140
141	data.dptr = (char *)&sessionid;
142	data.dsize = sizeof(sessionid);
143	if (tdb_store(tdb, key, data, tdb_store_flag) != 0) {
144		DEBUG(1,("session_claim: unable to create session id record\n"));
145		return False;
146	}
147
148	if (lp_utmp()) {
149		sys_utmp_claim(sessionid.username, sessionid.hostname,
150			       client_ip,
151			       sessionid.id_str, sessionid.id_num);
152	}
153
154	vuser->session_keystr = SMB_STRDUP(keystr);
155	if (!vuser->session_keystr) {
156		DEBUG(0, ("session_claim:  strdup() failed for session_keystr\n"));
157		return False;
158	}
159	return True;
160}
161
162/* called when a session is destroyed */
163void session_yield(user_struct *vuser)
164{
165	TDB_DATA dbuf;
166	struct sessionid sessionid;
167	struct in_addr *client_ip;
168	TDB_DATA key;
169
170	if (!tdb) return;
171
172	if (!vuser->session_keystr) {
173		return;
174	}
175
176	key.dptr = vuser->session_keystr;
177	key.dsize = strlen(vuser->session_keystr)+1;
178
179	dbuf = tdb_fetch(tdb, key);
180
181	if (dbuf.dsize != sizeof(sessionid))
182		return;
183
184	memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
185
186	client_ip = interpret_addr2(sessionid.ip_addr);
187
188	SAFE_FREE(dbuf.dptr);
189
190	if (lp_utmp()) {
191		sys_utmp_yield(sessionid.username, sessionid.hostname,
192			       client_ip,
193			       sessionid.id_str, sessionid.id_num);
194	}
195
196	smb_pam_close_session(sessionid.username, sessionid.id_str, sessionid.hostname);
197
198	tdb_delete(tdb, key);
199}
200
201static BOOL session_traverse(int (*fn)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *), void *state)
202{
203	if (!session_init()) {
204		DEBUG(3, ("No tdb opened\n"));
205		return False;
206	}
207
208	tdb_traverse(tdb, fn, state);
209	return True;
210}
211
212struct session_list {
213	int count;
214	struct sessionid *sessions;
215};
216
217static int gather_sessioninfo(TDB_CONTEXT *stdb, TDB_DATA kbuf, TDB_DATA dbuf,
218			      void *state)
219{
220	struct session_list *sesslist = (struct session_list *) state;
221	const struct sessionid *current = (const struct sessionid *) dbuf.dptr;
222
223	sesslist->count += 1;
224	sesslist->sessions = SMB_REALLOC_ARRAY(sesslist->sessions, struct sessionid,
225					sesslist->count);
226
227	memcpy(&sesslist->sessions[sesslist->count - 1], current,
228	       sizeof(struct sessionid));
229	DEBUG(7,("gather_sessioninfo session from %s@%s\n",
230		 current->username, current->remote_machine));
231	return 0;
232}
233
234int list_sessions(struct sessionid **session_list)
235{
236	struct session_list sesslist;
237
238	sesslist.count = 0;
239	sesslist.sessions = NULL;
240
241	if (!session_traverse(gather_sessioninfo, (void *) &sesslist)) {
242		DEBUG(3, ("Session traverse failed\n"));
243		SAFE_FREE(sesslist.sessions);
244		*session_list = NULL;
245		return 0;
246	}
247
248	*session_list = sesslist.sessions;
249	return sesslist.count;
250}
251