ncpl_conn.c revision 76176
1/*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *  $FreeBSD: head/lib/libncp/ncpl_conn.c 76176 2001-05-01 09:24:15Z markm $
33 */
34
35/*
36 *
37 * Current scheme to create/open connection:
38 * 1. ncp_li_init() - lookup -S [-U] options in command line
39 * 2. ncp_li_init() - try to find existing connection
40 * 3. ncp_li_init() - if no server name and no accessible connections - bail out
41 * 4. This is connection candidate, read .rc file, override with command line
42 *    and go ahead
43 * Note: connection referenced only via ncp_login() call. Although it is
44 * possible to get connection handle in other way, it will be unwise to use
45 * it, since conn can be destroyed at any time.
46 *
47 */
48#include <sys/param.h>
49#include <sys/lock.h>
50#include <sys/mutex.h>
51#include <sys/sysctl.h>
52#include <sys/ioctl.h>
53#include <sys/time.h>
54#include <sys/mount.h>
55#include <fcntl.h>
56#include <ctype.h>
57#include <errno.h>
58#include <stdio.h>
59#include <string.h>
60#include <stdlib.h>
61#include <pwd.h>
62#include <grp.h>
63#include <unistd.h>
64
65#include <netncp/ncp_lib.h>
66#include <netncp/ncp_rcfile.h>
67#include <nwfs/nwfs.h>
68
69static char *server_name; /* need a better way ! */
70
71
72
73int
74ncp_li_setserver(struct ncp_conn_loginfo *li, const char *arg) {
75	if (strlen(arg) >= NCP_BINDERY_NAME_LEN) {
76		ncp_error("server name '%s' too long", 0, arg);
77		return ENAMETOOLONG;
78	}
79	ncp_str_upper(strcpy(li->server, arg));
80	return 0;
81}
82
83int
84ncp_li_setuser(struct ncp_conn_loginfo *li, char *arg) {
85	if (arg && strlen(arg) >= NCP_BINDERY_NAME_LEN) {
86		ncp_error("user name '%s' too long", 0, arg);
87		return ENAMETOOLONG;
88	}
89	if (li->user)
90		free(li->user);
91	if (arg) {
92		li->user = strdup(arg);
93		if (li->user == NULL)
94			return ENOMEM;
95		ncp_str_upper(li->user);
96	} else
97		li->user = NULL;
98	return 0;
99}
100
101int
102ncp_li_setpassword(struct ncp_conn_loginfo *li, const char *passwd) {
103	if (passwd && strlen(passwd) >= 127) {
104		ncp_error("password too long", 0);
105		return ENAMETOOLONG;
106	}
107	if (li->password) {
108		bzero(li->password, strlen(li->password));
109		free(li->password);
110	}
111	if (passwd) {
112		li->password = strdup(passwd);
113		if (li->password == NULL)
114			return ENOMEM;
115	} else
116		li->password = NULL;
117	return 0;
118}
119/*
120 * Prescan command line for [-S server] [-U user] arguments
121 * and fill li structure with defaults
122 */
123int
124ncp_li_init(struct ncp_conn_loginfo *li, int argc, char *argv[]) {
125	int  opt, error = 0;
126	char *arg;
127
128	bzero(li,sizeof(*li));
129	li->timeout = 15;	/* these values should be large enough to handle */
130	li->retry_count = 4;	/* slow servers, even on ethernet */
131	li->access_mode = 0;
132	li->password = NULL;
133	li->sig_level = 1;
134	li->objtype = NCP_BINDERY_USER;
135	li->owner = NCP_DEFAULT_OWNER;
136	li->group = NCP_DEFAULT_GROUP;
137	server_name = NULL;
138	if (argv == NULL) return 0;
139	while (error == 0 && (opt = ncp_getopt(argc, argv, ":S:U:")) != -1) {
140		arg = ncp_optarg;
141		switch (opt) {
142		    case 'S':
143			error = ncp_li_setserver(li, arg);
144			break;
145		    case 'U':
146			error = ncp_li_setuser(li, arg);
147			break;
148		}
149	}
150	ncp_optind = ncp_optreset = 1;
151	return error;
152}
153
154void
155ncp_li_done(struct ncp_conn_loginfo *li) {
156	if (li->user)
157		free(li->user);
158	if (li->password)
159		free(li->password);
160}
161
162/*
163 * Lookup existing connection based on li structure, if connection
164 * found, it will be referenced. Otherwise full login sequence performed.
165 */
166int
167ncp_li_login(struct ncp_conn_loginfo *li, int *aconnid) {
168	int connHandle, error;
169
170	if ((error = ncp_conn_scan(li, &connHandle)) == 0) {
171		*aconnid = connHandle;
172		return 0;
173	}
174	error = ncp_connect(li, &connHandle);
175	if (error) return errno;
176	error = ncp_login(connHandle, li->user, li->objtype, li->password);
177	if (error) {
178		ncp_disconnect(connHandle);
179	} else
180		*aconnid = connHandle;
181	return error;
182}
183
184/*
185 * read rc file as follows:
186 * 1. read [server] section
187 * 2. override with [server:user] section
188 * Since abcence of rcfile is not a bug, silently ignore that fact.
189 * rcfile never closed to reduce number of open/close operations.
190 */
191int
192ncp_li_readrc(struct ncp_conn_loginfo *li) {
193	int i, val, error;
194	char uname[NCP_BINDERY_NAME_LEN*2+1];
195	char *sect = NULL, *p;
196
197	/*
198	 * if info from cmd line incomplete, try to find existing
199	 * connection and fill server/user from it.
200	 */
201	if (li->server[0] == 0 || li->user == NULL) {
202		int connHandle;
203		struct ncp_conn_stat cs;
204
205		if ((error = ncp_conn_scan(li, &connHandle)) != 0) {
206			ncp_error("no default connection found", errno);
207			return error;
208		}
209		ncp_conn_getinfo(connHandle, &cs);
210		ncp_li_setserver(li, cs.li.server);
211		ncp_li_setuser(li, cs.user);
212		ncp_li_setpassword(li, "");
213		ncp_disconnect(connHandle);
214	}
215	if (ncp_open_rcfile()) 	return 0;
216
217	for (i = 0; i < 2; i++) {
218		switch (i) {
219		    case 0:
220			sect = li->server;
221			break;
222		    case 1:
223			strcat(strcat(strcpy(uname,li->server),":"),li->user ? li->user : "default");
224			sect = uname;
225			break;
226		}
227		rc_getstringptr(ncp_rc, sect, "password", &p);
228		if (p)
229			ncp_li_setpassword(li, p);
230		rc_getint(ncp_rc,sect, "timeout", &li->timeout);
231		rc_getint(ncp_rc,sect, "retry_count", &li->retry_count);
232		rc_getint(ncp_rc,sect, "sig_level", &li->sig_level);
233		if (rc_getint(ncp_rc,sect,"access_mode",&val) == 0)
234			li->access_mode = val;
235		if(rc_getbool(ncp_rc,sect,"bindery",&val) == 0 && val) {
236			li->opt |= NCP_OPT_BIND;
237		}
238	}
239	return 0;
240}
241
242/*
243 * check for all uncompleted fields
244 */
245int
246ncp_li_check(struct ncp_conn_loginfo *li) {
247	int error = 0;
248	char *p;
249
250	do {
251		if (li->server[0] == 0) {
252			ncp_error("no server name specified", 0);
253			error = 1;
254			break;
255		}
256		error = ncp_find_fileserver(li,
257		    (server_name==NULL) ? AF_IPX : AF_INET, server_name);
258		if (error) {
259			ncp_error("can't find server %s", error, li->server);
260			break;
261		}
262		if (li->user == NULL || li->user[0] == 0) {
263			ncp_error("no user name specified for server %s",
264			    0, li->server);
265			error = 1;
266			break;
267		}
268		if (li->password == NULL) {
269			p = getpass("Netware password:");
270			error = ncp_li_setpassword(li, p) ? 1 : 0;
271		}
272	} while (0);
273	return error;
274}
275
276int
277ncp_conn_cnt(void) {
278	int error, cnt = 0, len = sizeof(cnt);
279
280#if __FreeBSD_version < 400001
281	error = sysctlbyname("net.ipx.ncp.conn_cnt", &cnt, &len, NULL, 0);
282#else
283	error = sysctlbyname("net.ncp.conn_cnt", &cnt, &len, NULL, 0);
284#endif
285	if (error) cnt = 0;
286	return cnt;
287}
288
289/*
290 * Find an existing connection and reference it
291 */
292int
293ncp_conn_find(char *server,char *user) {
294	struct ncp_conn_args ca;
295	int connid, error;
296
297	if (server == NULL && user == NULL) {
298		error = ncp_conn_scan(NULL,&connid);
299		if (error) return -2;
300		return connid;
301	}
302	if (server == NULL)
303		return -2;
304	ncp_str_upper(server);
305	if (user) ncp_str_upper(user);
306	bzero(&ca, sizeof(ca));
307	ncp_li_setserver(&ca, server);
308	ncp_li_setuser(&ca, user);
309	error = ncp_conn_scan(&ca,&connid);
310	if (error)
311		connid = -1;
312	return connid;
313}
314
315int
316ncp_li_arg(struct ncp_conn_loginfo *li, int opt, char *arg) {
317	int error = 0, sig_level;
318	char *p, *cp;
319	struct group *gr;
320	struct passwd *pw;
321
322	switch(opt) {
323	    case 'S': /* we already fill server/[user] pair */
324	    case 'U':
325		break;
326	    case 'A':
327		server_name = arg;
328		break;
329	    case 'B':
330		li->opt |= NCP_OPT_BIND;
331		break;
332	    case 'C':
333		li->opt |= NCP_OPT_NOUPCASEPASS;
334		break;
335	    case 'I':
336		sig_level = atoi(arg);
337		if (sig_level < 0 || sig_level > 3) {
338			ncp_error("invalid NCP signature level option `%s'\
339			    (must be a number between 0 and 3)", 0, arg);
340			error = 1;
341		}
342		li->sig_level = sig_level;
343		if (sig_level > 1) li->opt |= NCP_OPT_SIGN;
344		break;
345	    case 'M':
346		li->access_mode = strtol(arg, NULL, 8);
347		break;
348	    case 'N':
349		ncp_li_setpassword(li, "");
350		break;
351	    case 'O':
352		p = strdup(arg);
353		cp = strchr(p, ':');
354		if (cp) {
355			*cp++ = '\0';
356			if (*cp) {
357				gr = getgrnam(cp);
358				if (gr) {
359					li->group = gr->gr_gid;
360				} else
361					ncp_error("Invalid group name %s, ignored",
362					    0, cp);
363			}
364		}
365		if (*p) {
366			pw = getpwnam(p);
367			if (pw) {
368				li->owner = pw->pw_uid;
369			} else
370				ncp_error("Invalid user name %s, ignored", 0, p);
371		}
372		endpwent();
373		free(p);
374		break;
375	    case 'P':
376		li->opt |= NCP_OPT_PERMANENT;
377		break;
378	    case 'R':
379		li->retry_count = atoi(arg);
380		break;
381	    case 'W':
382		li->timeout = atoi(arg);
383		break;
384	}
385	return error;
386}
387
388void *
389ncp_conn_list(void) {
390	int error, cnt = 0, len;
391	void *p;
392
393	cnt = ncp_conn_cnt();
394	if (cnt == 0) return NULL;
395	len = cnt*(sizeof(struct ncp_conn_stat))+sizeof(int);
396	p = malloc(len);
397	if (p == NULL) return NULL;
398#if __FreeBSD_version < 400001
399	error = sysctlbyname("net.ipx.ncp.conn_stat", p, &len, NULL, 0);
400#else
401	error = sysctlbyname("net.ncp.conn_stat", p, &len, NULL, 0);
402#endif
403	if (error) {
404		free(p);
405		p = NULL;
406	}
407	return p;
408}
409
410
411int
412ncp_conn_setflags(int connid, u_int16_t mask, u_int16_t flags) {
413	int error;
414	DECLARE_RQ;
415
416	ncp_init_request(conn);
417	ncp_add_byte(conn, NCP_CONN_SETFLAGS);
418	ncp_add_word_lh(conn, mask);
419	ncp_add_word_lh(conn, flags);
420	if ((error = ncp_conn_request(connid, conn)) < 0)
421		return -1;
422	return error;
423}
424
425int
426ncp_login(int connHandle, const char *user, int objtype, const char *password) {
427	int error;
428	struct ncp_conn_login *p;
429	DECLARE_RQ;
430
431	ncp_init_request(conn);
432	ncp_add_byte(conn, NCP_CONN_LOGIN);
433	p = (struct ncp_conn_login *)&conn->packet[conn->rqsize];
434	(const char*)p->username = user;
435	p->objtype = objtype;
436	(const char*)p->password = password;
437	conn->rqsize += sizeof(*p);
438	if ((error = ncp_conn_request(connHandle, conn)) < 0)
439		return -1;
440	return error;
441}
442
443int
444ncp_connect_addr(struct sockaddr *sa, NWCONN_HANDLE *chp) {
445	int error;
446	struct ncp_conn_args li;
447
448	bzero(&li, sizeof(li));
449	bcopy(sa, &li.addr, sa->sa_len);
450	/*
451	 * XXX Temporary !!!. server will be filled in kernel !!!
452	 */
453	strcpy(li.server,ipx_ntoa(li.ipxaddr.sipx_addr));
454	error = ncp_connect(&li, chp);
455	return error;
456}
457
458int
459ncp_conn_getinfo(int connHandle, struct ncp_conn_stat *ps) {
460	int error;
461	DECLARE_RQ;
462
463	ncp_init_request(conn);
464	ncp_add_byte(conn, NCP_CONN_GETINFO);
465	if ((error = ncp_conn_request(connHandle, conn)) < 0)
466		return -1;
467	memcpy(ps, ncp_reply_data(conn,0), sizeof(*ps));
468	return error;
469}
470
471int
472ncp_conn_getuser(int connHandle, char **user) {
473	int error;
474	DECLARE_RQ;
475
476	ncp_init_request(conn);
477	ncp_add_byte(conn, NCP_CONN_GETUSER);
478	if ((error = ncp_conn_request(connHandle, conn)) < 0)
479		return -1;
480	*user = strdup(ncp_reply_data(conn,0));
481	return error;
482}
483
484int
485ncp_conn2ref(int connHandle, int *connRef) {
486	int error;
487	DECLARE_RQ;
488
489	ncp_init_request(conn);
490	ncp_add_byte(conn, NCP_CONN_CONN2REF);
491	if ((error = ncp_conn_request(connHandle, conn)) < 0)
492		return -1;
493	*connRef = *((int*)ncp_reply_data(conn,0));
494	return error;
495}
496
497int
498ncp_path2conn(char *path, int *connHandle) {
499	struct statfs st;
500	int d, error;
501
502	if ((error = statfs(path, &st)) != 0) return errno;
503	if (strcmp(st.f_fstypename,"nwfs") != 0) return EINVAL;
504	if ((d = open(path, O_RDONLY)) < 0) return errno;
505	if ((error = ioctl(d,NWFSIOC_GETCONN, connHandle)) != 0) return errno;
506	close(d);
507	return 0;
508}
509
510int
511ncp_conn_dup(NWCONN_HANDLE org, NWCONN_HANDLE *res) {
512	int error;
513	DECLARE_RQ;
514
515	ncp_init_request(conn);
516	ncp_add_byte(conn, NCP_CONN_DUP);
517	if ((error = ncp_conn_request(org, conn)) < 0)
518		return errno;
519	*res = *((int*)ncp_reply_data(conn, 0));
520	return 0;
521}
522