FreeBSD/Linux Kernel Cross Reference
sys/netncp/ncp_ncp.c
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$
33 *
34 * Core of NCP protocol
35 */
36 #include "opt_inet.h"
37 #include "opt_ipx.h"
38
39 #include <sys/param.h>
40 #include <sys/errno.h>
41 #include <sys/systm.h>
42 #include <sys/proc.h>
43 #include <sys/poll.h>
44 #include <sys/signalvar.h>
45 #include <sys/mbuf.h>
46
47 #ifdef IPX
48 #include <netipx/ipx.h>
49 #include <netipx/ipx_var.h>
50 #endif
51
52 #include <netncp/ncp.h>
53 #include <netncp/ncp_conn.h>
54 #include <netncp/ncp_sock.h>
55 #include <netncp/ncp_subr.h>
56 #include <netncp/ncp_ncp.h>
57 #include <netncp/ncp_rq.h>
58 #include <netncp/nwerror.h>
59
60 static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp);
61 static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target);
62 static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options);
63 static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
64
65
66 #ifdef NCP_DATA_DEBUG
67 static
68 void m_dumpm(struct mbuf *m) {
69 char *p;
70 int len;
71 printf("d=");
72 while(m) {
73 p=mtod(m,char *);
74 len=m->m_len;
75 printf("(%d)",len);
76 while(len--){
77 printf("%02x ",((int)*(p++)) & 0xff);
78 }
79 m=m->m_next;
80 };
81 printf("\n");
82 }
83 #endif /* NCP_DATA_DEBUG */
84
85 int
86 ncp_chkintr(struct ncp_conn *conn, struct proc *p)
87 {
88 sigset_t tmpset;
89
90 if (p == NULL)
91 return 0;
92 tmpset = p->p_siglist;
93 SIGSETNAND(tmpset, p->p_sigmask);
94 SIGSETNAND(tmpset, p->p_sigignore);
95 if (SIGNOTEMPTY(p->p_siglist) && NCP_SIGMASK(tmpset))
96 return EINTR;
97 return 0;
98 }
99
100 /*
101 * Process initial NCP handshake (attach)
102 * NOTE: Since all functions below may change conn attributes, they
103 * should be called with LOCKED connection, also they use procp & ucred
104 */
105 int
106 ncp_ncp_connect(struct ncp_conn *conn) {
107 int error;
108 struct ncp_rphdr *rp;
109 DECLARE_RQ;
110
111 conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
112 conn->seq = 0;
113 checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->procp,conn->ucred));
114 error=ncp_do_request(conn,rqp);
115 if (!error) {
116 rp = mtod(rqp->rp, struct ncp_rphdr*);
117 conn->connid = rp->conn_low + (rp->conn_high << 8);
118 }
119 ncp_rq_done(rqp);
120 if (error) return error;
121 conn->flags |= NCPFL_ATTACHED;
122
123 error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0);
124 if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
125 printf("Unable to negotiate requested security level\n");
126 error = EOPNOTSUPP;
127 }
128 if (error) {
129 ncp_ncp_disconnect(conn);
130 return error;
131 }
132 #ifdef NCPBURST
133 ncp_burst_connect(conn);
134 #endif
135 bad:
136 return error;
137 }
138
139 int
140 ncp_ncp_disconnect(struct ncp_conn *conn) {
141 int error;
142 struct ncp_rqhdr *ncprq;
143 DECLARE_RQ;
144
145 NCPSDEBUG("for connid=%d\n",conn->nc_id);
146 #ifdef NCPBURST
147 ncp_burst_disconnect(conn);
148 #endif
149 error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->procp,conn->ucred);
150 ncprq = mtod(rqp->rq,struct ncp_rqhdr*);
151 error=ncp_do_request(conn,rqp);
152 ncp_rq_done(rqp);
153 ncp_conn_invalidate(conn);
154 ncp_sock_disconnect(conn);
155 return 0;
156 }
157 /*
158 * Make a signature for the current packet and add it at the end of the
159 * packet.
160 */
161 static void
162 ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
163 u_char data[64];
164
165 bzero(data, sizeof(data));
166 bcopy(conn->sign_root, data, 8);
167 setdle(data, 8, *size);
168 m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1,
169 min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12);
170 ncp_sign(conn->sign_state, data, conn->sign_state);
171 ncp_rq_mem(rqp, (void*)conn->sign_state, 8);
172 (*size) += 8;
173 }
174
175 /*
176 * Low level send rpc, here we do not attempt to restore any connection,
177 * Connection expected to be locked
178 */
179 static int
180 ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
181 int error=EIO,len, dosend, plen = 0, gotpacket, s;
182 struct socket *so;
183 struct proc *p = conn->procp;
184 struct ncp_rqhdr *rq;
185 struct ncp_rphdr *rp=NULL;
186 struct timeval tv;
187 struct mbuf *m, *mreply = NULL;
188
189 conn->nc_rq = rqp;
190 rqp->conn = conn;
191 if (p == NULL)
192 p = curproc; /* XXX maybe procpage ? */
193 if (!ncp_conn_valid(conn)) {
194 printf("%s: conn not valid\n",__FUNCTION__);
195 return (error);
196 }
197 so = conn->ncp_so;
198 if (!so) {
199 printf("%s: ncp_so is NULL !\n",__FUNCTION__);
200 ncp_conn_invalidate(conn); /* wow ! how we do that ? */
201 return EBADF;
202 }
203 /*
204 * Flush out replies on previous reqs
205 */
206 s = splnet();
207 while (1/*so->so_rcv.sb_cc*/) {
208 if (ncp_poll(so,POLLIN) == 0) break;
209 if (ncp_sock_recv(so,&m,&len) != 0) break;
210 m_freem(m);
211 }
212 rq = mtod(rqp->rq,struct ncp_rqhdr *);
213 rq->seq = conn->seq;
214 m = rqp->rq;
215 len = 0;
216 while (m) {
217 len += m->m_len;
218 m = m->m_next;
219 }
220 rqp->rq->m_pkthdr.len = len;
221 switch(rq->fn) {
222 case 0x15: case 0x16: case 0x17: case 0x23:
223 m = rqp->rq;
224 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
225 break;
226 }
227 if (conn->flags & NCPFL_SIGNACTIVE) {
228 ncp_sign_packet(conn, rqp, &len);
229 rqp->rq->m_pkthdr.len = len;
230 }
231 rq->conn_low = conn->connid & 0xff;
232 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
233 /* XXX: this is temporary fix till I find a better solution */
234 rq->task = rq->conn_low;
235 rq->conn_high = conn->connid >> 8;
236 rqp->rexmit = conn->li.retry_count;
237 for(dosend = 1;;) {
238 if (rqp->rexmit-- == 0) {
239 error = ETIMEDOUT;
240 break;
241 }
242 error = 0;
243 if (dosend) {
244 NCPSDEBUG("send:%04x f=%02x c=%d l=%d s=%d t=%d\n",rq->type, rq->fn, (rq->conn_high << 8) + rq->conn_low,
245 rqp->rq->m_pkthdr.len, rq->seq, rq->task
246 );
247 error = ncp_sock_send(so, rqp->rq, rqp);
248 if (error) break;
249 }
250 tv.tv_sec = conn->li.timeout;
251 tv.tv_usec = 0;
252 error = ncp_sock_rselect(so, p, &tv, POLLIN);
253 if (error == EWOULDBLOCK ) /* timeout expired */
254 continue;
255 error = ncp_chkintr(conn, p);
256 if (error == EINTR) /* we dont restart */
257 break;
258 if (error) break;
259 /*
260 * At this point it is possible to get more than one
261 * reply from server. In general, last reply should be for
262 * current request, but not always. So, we loop through
263 * all replies to find the right answer and flush others.
264 */
265 gotpacket = 0; /* nothing good found */
266 dosend = 1; /* resend rq if error */
267 for (;;) {
268 error = 0;
269 if (ncp_poll(so,POLLIN) == 0) break;
270 /* if (so->so_rcv.sb_cc == 0) {
271 break;
272 }*/
273 error = ncp_sock_recv(so,&m,&len);
274 if (error) break; /* must be more checks !!! */
275 if (m->m_len < sizeof(*rp)) {
276 m = m_pullup(m, sizeof(*rp));
277 if (m == NULL) {
278 printf("%s: reply too short\n",__FUNCTION__);
279 continue;
280 }
281 }
282 rp = mtod(m, struct ncp_rphdr*);
283 if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
284 NCPSDEBUG("got positive acknowledge\n");
285 m_freem(m);
286 rqp->rexmit = conn->li.retry_count;
287 dosend = 0; /* server just busy and will reply ASAP */
288 continue;
289 }
290 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
291 (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
292 rp->completion_code, rp->connection_state);
293 NCPDDEBUG(m);
294 if ( (rp->type == NCP_REPLY) &&
295 ((rq->type == NCP_ALLOC_SLOT) ||
296 ((rp->conn_low == rq->conn_low) &&
297 (rp->conn_high == rq->conn_high)
298 ))) {
299 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
300 dosend = 1;
301 }
302 if (rp->seq == rq->seq) {
303 if (gotpacket) {
304 m_freem(m);
305 } else {
306 gotpacket = 1;
307 mreply = m;
308 plen = len;
309 }
310 continue; /* look up other for other packets */
311 }
312 }
313 m_freem(m);
314 NCPSDEBUG("reply mismatch\n");
315 } /* for receive */
316 if (error) break;
317 if (gotpacket) break;
318 /* try to resend, or just wait */
319 }
320 splx(s);
321 conn->seq++;
322 if (error) {
323 NCPSDEBUG("error=%d\n",error);
324 if (error != EINTR) /* if not just interrupt */
325 ncp_conn_invalidate(conn); /* only reconnect to restore */
326 return(error);
327 }
328 if (conn->flags & NCPFL_SIGNACTIVE) {
329 /* XXX: check reply signature */
330 m_adj(mreply, -8);
331 plen -= 8;
332 }
333 len = plen;
334 m = mreply;
335 rp = mtod(m, struct ncp_rphdr*);
336 len -= sizeof(*rp);
337 rqp->rpsize = len;
338 rqp->cc = error = rp->completion_code;
339 if (error) error |= 0x8900; /* server error */
340 rqp->cs = rp->connection_state;
341 if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
342 NCPSDEBUG("server drop us\n");
343 ncp_conn_invalidate(conn);
344 error = ECONNRESET;
345 }
346 rqp->rp = m;
347 rqp->mrp = m;
348 rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
349 return error;
350 }
351
352 /*
353 * Here we will try to restore any loggedin & dropped connection,
354 * connection should be locked on entry
355 */
356 int ncp_restore_login(struct ncp_conn *conn);
357 int
358 ncp_restore_login(struct ncp_conn *conn) {
359 int error, oldflags;
360
361 if (conn->flags & NCPFL_RESTORING) {
362 printf("Hey, ncp_restore_login called twise !!!\n");
363 return 0;
364 }
365 oldflags = conn->flags;
366 printf("Restoring connection, flags = %d\n",oldflags);
367 if ((oldflags & NCPFL_LOGGED) == 0) {
368 return ECONNRESET; /* no need to restore empty conn */
369 }
370 conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
371 conn->flags |= NCPFL_RESTORING;
372 do { /* not a loop */
373 error = ncp_reconnect(conn);
374 if (error) break;
375 if (conn->li.user)
376 error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred);
377 if (error) break;
378 conn->flags |= NCPFL_LOGGED;
379 } while(0);
380 if (error) {
381 conn->flags = oldflags | NCPFL_INVALID;
382 }
383 conn->flags &= ~NCPFL_RESTORING;
384 return error;
385 }
386
387 int
388 ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
389 int error, rcnt;
390 /* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
391
392 error = ncp_conn_lock(conn,rqp->p,rqp->cred,NCPM_EXECUTE);
393 if (error) return error;
394 rcnt = NCP_RESTORE_COUNT;
395 for(;;) {
396 if (!ncp_conn_valid(conn)) {
397 if (rcnt==0) {
398 error = ECONNRESET;
399 break;
400 }
401 rcnt--;
402 error = ncp_restore_login(conn);
403 if (error)
404 continue;
405 }
406 error=ncp_do_request(conn, rqp);
407 if (ncp_conn_valid(conn)) /* not just error ! */
408 break;
409 }
410 ncp_conn_unlock(conn,rqp->p);
411 return error;
412 }
413
414 /*
415 * All negotiation functions expect a locked connection
416 */
417 static int
418 ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
419 int error;
420 DECLARE_RQ;
421
422 NCP_RQ_HEAD(0x21,conn->procp,conn->ucred);
423 ncp_rq_word_hl(rqp, size);
424 checkbad(ncp_request(conn,rqp));
425 *target = min(ncp_rp_word_hl(rqp), size);
426 NCP_RQ_EXIT;
427 return error;
428 }
429
430 static int
431 ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
432 int *ret_size, int *ret_options) {
433 int error;
434 int rs;
435 DECLARE_RQ;
436
437 NCP_RQ_HEAD(0x61,conn->procp,conn->ucred);
438 ncp_rq_word_hl(rqp, size);
439 ncp_rq_byte(rqp, options);
440 checkbad(ncp_request(conn, rqp));
441 rs = ncp_rp_word_hl(rqp);
442 *ret_size = (rs == 0) ? size : min(rs, size);
443 ncp_rp_word_hl(rqp); /* skip echo socket */
444 *ret_options = ncp_rp_byte(rqp);
445 NCP_RQ_EXIT;
446 return error;
447 }
448
449 static int
450 ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
451 {
452 int neg_buffsize, error, options, sl;
453
454 sl = conn->li.sig_level;
455 if (sl >= 2)
456 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
457 #ifdef IPX
458 if (ipxcksum == 2)
459 in_options |= NCP_IPX_CHECKSUM;
460 #endif
461 error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
462 &neg_buffsize, &options);
463 if (!error) {
464 #ifdef IPX
465 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
466 if (ipxcksum == 2) {
467 printf("Server refuses to support IPX checksums\n");
468 return NWE_REQUESTER_FAILURE;
469 }
470 in_options |= NCP_IPX_CHECKSUM;
471 error = 1;
472 }
473 #endif /* IPX */
474 if ((options ^ in_options) & 2) {
475 if (sl == 0 || sl == 3)
476 return NWE_SIGNATURE_LEVEL_CONFLICT;
477 if (sl == 1) {
478 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
479 error = 1;
480 }
481 }
482 if (error) {
483 error = ncp_negotiate_size_and_options(conn,
484 buffsize, in_options, &neg_buffsize, &options);
485 if ((options ^ in_options) & 3) {
486 return NWE_SIGNATURE_LEVEL_CONFLICT;
487 }
488 }
489 } else {
490 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
491 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
492 &neg_buffsize);
493 }
494 if (error) return error;
495 if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
496 return EINVAL;
497 conn->buffer_size = neg_buffsize;
498 if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
499 conn->flags |= NCPFL_SIGNWANTED;
500 #ifdef IPX
501 ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
502 #endif
503 return 0;
504 }
505
506 int
507 ncp_reconnect(struct ncp_conn *conn) {
508 int error;
509
510 /* close any open sockets */
511 ncp_sock_disconnect(conn);
512 switch( conn->li.saddr.sa_family ) {
513 #ifdef IPX
514 case AF_IPX:
515 error = ncp_sock_connect_ipx(conn);
516 break;
517 #endif
518 #ifdef INET
519 case AF_INET:
520 error = ncp_sock_connect_in(conn);
521 break;
522 #endif
523 default:
524 return EPROTONOSUPPORT;
525 }
526 if (!error)
527 error = ncp_ncp_connect(conn);
528 return error;
529 }
530
531 /*
532 * Create conn structure and try to do low level connect
533 * Server addr should be filled in.
534 */
535 int
536 ncp_connect(struct ncp_conn_args *li, struct proc *p, struct ucred *cred,
537 struct ncp_conn **aconn)
538 {
539 struct ncp_conn *conn;
540 struct ucred *owner;
541 int error, isroot;
542
543 if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
544 return EPROTONOSUPPORT;
545 isroot = ncp_suser(cred) == 0;
546 /*
547 * Only root can change ownership
548 */
549 if (li->owner != NCP_DEFAULT_OWNER && !isroot)
550 return EPERM;
551 if (li->group != NCP_DEFAULT_GROUP &&
552 !groupmember(li->group, cred) && !isroot)
553 return EPERM;
554 if (li->owner != NCP_DEFAULT_OWNER) {
555 owner = crget();
556 owner->cr_uid = li->owner;
557 } else {
558 owner = cred;
559 crhold(owner);
560 }
561 error = ncp_conn_alloc(p, owner, &conn);
562 if (error)
563 return (error);
564 if (error) {
565 ncp_conn_free(conn);
566 return error;
567 }
568 conn->li = *li;
569 conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ?
570 li->group : cred->cr_groups[0];
571
572 if (li->retry_count == 0)
573 conn->li.retry_count = NCP_RETRY_COUNT;
574 if (li->timeout == 0)
575 conn->li.timeout = NCP_RETRY_TIMEOUT;
576 error = ncp_reconnect(conn);
577 if (error) {
578 ncp_disconnect(conn);
579 } else {
580 *aconn=conn;
581 }
582 return error;
583 }
584 /*
585 * Break connection and deallocate memory
586 */
587 int
588 ncp_disconnect(struct ncp_conn *conn) {
589
590 if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
591 return EACCES;
592 if (conn->ref_cnt != 0) return EBUSY;
593 if (conn->flags & NCPFL_PERMANENT) return EBUSY;
594 if (ncp_conn_valid(conn)) {
595 ncp_ncp_disconnect(conn);
596 }
597 ncp_sock_disconnect(conn);
598 ncp_conn_free(conn);
599 return 0;
600 }
601
602 void
603 ncp_check_rq(struct ncp_conn *conn){
604 return;
605 if (conn->flags & NCPFL_INTR) return;
606 /* first, check for signals */
607 if (ncp_chkintr(conn,conn->procp)) {
608 conn->flags |= NCPFL_INTR;
609 }
610 return;
611 }
Cache object: 88961da90a886a170bbe02c8d5cad197
|