tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: tftp protocol



On May 31, 12:05pm, Patrick Welche wrote:
} On Thu, Jan 07, 2010 at 06:28:06AM -0800, John Nemeth wrote:
} > On May 29,  1:18pm, Patrick Welche wrote:
} >      Whilst I think the client should be fixed, this option is
} > non-contentious.  The only thing is I would check to see if any of the
} > other BSDs supports and use the same option letter.
} 
} Free/Open don't use 'p', and don't offer similar functionality.

     Good enough.

} >      The block counter should wrap as is done by both FreeBSD and
} > OpenBSD.  This is most likely necessary to support downloading large
} > files on certain devices (i.e. Cisco devices running IOS).  I need to
} > further investigate this, but will most likely be implementing this
} > change.
} 
} The attached patch does wrap to zero (successfully booted a vista image, and
} the wrap warning appeared in the logs.
} 
} Index: tftpd.8
} ===================================================================
} RCS file: /cvsroot/src/libexec/tftpd/tftpd.8,v
} retrieving revision 1.21
} diff -u -r1.21 tftpd.8
} --- tftpd.8   7 Aug 2003 09:46:53 -0000       1.21
} +++ tftpd.8   8 Jan 2010 17:16:46 -0000
} @@ -29,7 +29,7 @@
}  .\"
}  .\"  from: @(#)tftpd.8       8.1 (Berkeley) 6/4/93
}  .\"
} -.Dd June 11, 2003
} +.Dd November 13, 2009

     This will get changed to the actual commit date.

}  .Dt TFTPD 8
}  .Os
}  .Sh NAME
} @@ -43,6 +43,7 @@
}  .Op Fl g Ar group
}  .Op Fl l
}  .Op Fl n
} +.Op Fl p Ar path separator
}  .Op Fl s Ar directory
}  .Op Fl u Ar user
}  .Op Ar directory ...
} @@ -107,6 +108,11 @@
}  .It Fl n
}  Suppresses negative acknowledgement of requests for nonexistent
}  relative filenames.
} +.It Fl p Ar path separator
} +All occurances of the single character
} +.Ar path separator
} +in the requested filename are replaced with
} +.Sq / .
}  .It Fl s Ar directory
}  .Nm
}  will
} @@ -193,11 +199,15 @@
}  and first appeared in
}  .Nx 2.0 .
}  .Sh BUGS
} -Files larger than 33488896 octets (65535 blocks) cannot be transferred
} -without client and server supporting blocksize negotiation (RFCs
} -2347 and 2348).
} -.Pp
} -Many tftp clients will not transfer files over 16744448 octets (32767 
blocks).
} +Files larger than 33,553,919 octets (65535 blocks, last one <512
} +octets) cannot be correctly transferred without client and server
} +supporting blocksize negotiation (RFCs 2347 and 2348). As a kludge,
} +.Nm
} +accepts a sequence of block numbers which wrap to zero after 65535.

     I'm not sure I'm crazy about the wording here, but I'm not sure
how I would fix it up.  I would probably make it a couple of sentences
and say something about legacy clients that don't support options.

} +.Pp
} +Many tftp clients will not transfer files over 16,776,703 octets
} +(32767 blocks), as they incorrectly count the block number using
} +a signed rather than unsigned 16-bit integer.
}  .Sh SECURITY CONSIDERATIONS
}  You are
}  .Em strongly
} Index: tftpd.c
} ===================================================================
} RCS file: /cvsroot/src/libexec/tftpd/tftpd.c,v
} retrieving revision 1.32
} diff -u -r1.32 tftpd.c
} --- tftpd.c   16 Mar 2009 01:56:21 -0000      1.32
} +++ tftpd.c   8 Jan 2010 17:16:47 -0000
} @@ -107,6 +107,7 @@
}  static int   suppress_naks;
}  static int   logging;
}  static int   secure;
} +static char  pathsep = '\0';
}  static char  *securedir;
}  
}  struct formats;
} @@ -141,7 +142,7 @@
}  {
}  
}       syslog(LOG_ERR,
} -    "Usage: %s [-dln] [-u user] [-g group] [-s directory] [directory ...]",
} +    "Usage: %s [-dln] [-u user] [-g group] [-s directory] [-p pathsep] 
[directory ...]",
}                   getprogname());
}       exit(1);
}  }
} @@ -170,7 +171,7 @@
}       curuid = getuid();
}       curgid = getgid();
}  
} -     while ((ch = getopt(argc, argv, "dg:lns:u:")) != -1)
} +     while ((ch = getopt(argc, argv, "dg:lnp:s:u:w:")) != -1)

     What's this -w option?

}               switch (ch) {
}               case 'd':
}                       debug++;
} @@ -188,6 +189,11 @@
}                       suppress_naks = 1;
}                       break;
}  
} +             case 'p':
} +                     pathsep = optarg[0];
} +                     if (optarg[1] != '\0' || optarg[0] == '\0') usage();

     usage() should be on the next line to make the code easier to read.

} +                     break;
} +
}               case 's':
}                       secure = 1;
}                       securedir = optarg;
} @@ -662,6 +668,15 @@
}                       exit(1);
}               }
}       }
} +     /*
} +      * Globally replace the path separator given in the -p option
} +      * with / to cope with clients expecting a non-unix path separator.
} +      */
} +     if (pathsep != '\0') {
} +             for (cp = filename; *cp != '\0'; ++cp) {
} +                     if (*cp == pathsep) *cp = '/';
} +             }
} +     }
}       ecode = (*pf->f_validate)(&filename, tp->th_opcode);
}       if (logging) {
}               syslog(LOG_INFO, "%s: %s request for %s: %s",
} @@ -853,7 +868,7 @@
}       case OACK:
}               return "OACK";
}       default:
} -             (void)snprintf(obuf, sizeof(obuf), "*code %d*", code);
} +             (void)snprintf(obuf, sizeof(obuf), "*code 0x%x*", code);
}               return obuf;
}       }
}  }
} @@ -918,20 +933,20 @@
}                               goto abort;
}  
}                       case ACK:
} -                             if (ap->th_block == 0) {
} +                             if (etftp && ap->th_block == 0) {
}                                       etftp = 0;
}                                       acklength = 0;
}                                       dp = r_init();
}                                       goto done;
}                               }
} -                             if (ap->th_block == block)
} +                             if (ap->th_block == (u_short)block)

     I would use uint16_t to make it clear what size is wanted.  If you
need a specific size, it's much better to specify it then make
assumptions about what size a type is.

}                                       goto done;
}                               if (debug)
}                                       syslog(LOG_DEBUG, "Resync ACK %u != %u",
}                                           (unsigned int)ap->th_block, block);
}                               /* Re-synchronize with the other side */
}                               (void) synchnet(peer, tftp_blksize);
} -                             if (ap->th_block == (block -1))
} +                             if (ap->th_block == (u_short)(block - 1))

     Same thing, use uint16_t.

}                                       goto send_data;
}                       default:
}                               syslog(LOG_INFO, "Received %s in sendfile\n",
} @@ -942,6 +957,9 @@
}  done:
}               if (debug)
}                       syslog(LOG_DEBUG, "Received ACK for block %u", block);
} +             if (block == UINT16_MAX && size == tftp_blksize)
} +                     syslog(LOG_WARNING,
} +                     "Block number wrapped (hint: increase block size)");

     Don't bother with the hint since the client is usually out of the
sysadmin's control.  Maybe also use LOG_DEBUG since this is normal
operation for the clients that do it.

}               block++;
}       } while (size == tftp_blksize || block == 1);
}  abort:
} @@ -980,6 +998,9 @@
}               }
}               if (debug)
}                       syslog(LOG_DEBUG, "Sending ACK for block %u\n", block);
} +             if (block == UINT16_MAX)
} +                     syslog(LOG_WARNING,
} +                     "Block number wrapped (hint: increase block size)");

     Where's the wrap?  block isn't being wrapped back to 0 nor are
comparisons being done against short/uint16_t.  It looks like this will
die at line 1011 when the block number from the client is compared
against 65536.  Have you tried uploading a large file?

}               block++;
}               (void) setjmp(timeoutbuf);
}  send_ack:

     The only other thing I would do is make wrapping an option in
order to satisfy the purists.  With the option, maybe also make it
possible to wrap to 1 instead of 0, since apparently there are some
clients that expect that.  However, I don't think that's of vital
importance for the first pass.

}-- End of excerpt from Patrick Welche


Home | Main Index | Thread Index | Old Index