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