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