Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / netproto / ncp / 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: src/sys/netncp/ncp_ncp.c,v 1.3 1999/10/29 10:21:07 bp Exp $
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 }