diff --git a/CMakeLists.txt b/CMakeLists.txt index af7ea72..4e2ee53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ if(MARS_NWE_BUILD_DOSUTILS) slist.c flag.c flagdir.c + rights.c c32ncp.c nwcrypt.c nwdebug.c diff --git a/README.md b/README.md index 603b8ff..0b37425 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Still to validate or continue: - Server listing through `SLIST` - File attribute management through `FLAG` - Directory attribute management through `FLAGDIR` +- Effective rights display through `RIGHTS` - Optional mars_nwe debug control hooks - Developer diagnostics through `TESTS` @@ -63,6 +64,7 @@ The current command dispatcher includes these built-ins: - `SLIST` - `FLAG` - `FLAGDIR` +- `RIGHTS` - `DEBUG` - `ECHO` - `CD` @@ -114,6 +116,7 @@ This path is currently used by: - `FLAG` - `FLAGDIR` +- `RIGHTS` The old `Net_Call` / INT 21h requester path is kept as a fallback where appropriate, but Client32 is now preferred for the validated FLAG-family operations. @@ -363,6 +366,30 @@ MARS/SYS:UDIR `Private` is intentionally rejected for the current NetWare 386-style path. + +### `RIGHTS` + +Display effective NetWare rights for a file or directory. + +Typical usage: + +```text +RIGHTS [path] +``` + +Supported in this first version: + +- directory paths +- file paths, using the parent directory rights for the first read-only implementation +- Novell-like display of the effective rights mask + +Rights are shown in the traditional order: + +```text +S R W C E M F A +Supervisor, Read, Write, Create, Erase, Modify, File scan, Access Control +``` + ### `DEBUG` Set mars_nwe debug levels for selected server-side modules. diff --git a/c32ncp.c b/c32ncp.c index 475e3f3..b3b56a3 100644 --- a/c32ncp.c +++ b/c32ncp.c @@ -97,6 +97,85 @@ static UI c32_build_handle_path(uint8 *buf, uint8 dhandle, return(used); } + +static UI c32_build_handle_path_from_dos_path(uint8 *buf, uint8 dhandle, + uint16 dirbase, uint8 style, + const char *dospath) +{ + uint8 *p; + uint8 *countp; + int count = 0; + const char *s; + UI used; + + memset(buf, 0, 0x140); + + if (dhandle) { + c32_put_word_lh(buf + 1, (uint16)dhandle); + c32_put_word_lh(buf + 3, dirbase); + buf[5] = style; + } else { + buf[5] = 0xff; + } + + p = buf + 6; + countp = p++; + + s = dospath; + if (!s) s = ""; + + /* + * DOS tools mostly pass relative paths against the current directory + * handle. Accept simple DOS decoration here so RIGHTS can pass "." or + * ".\\UDIR\\FILE" without constructing path components in the caller. + */ + if (s[0] && s[1] == ':') + s += 2; + + while (*s == '\\' || *s == '/') + s++; + + while (*s && p < buf + 0x138 && count < 32) { + const char *start; + int len; + + while (*s == '\\' || *s == '/') + s++; + + if (*s == '.' + && (s[1] == '\0' || s[1] == '\\' || s[1] == '/')) { + s++; + continue; + } + + start = s; + while (*s && *s != '\\' && *s != '/') + s++; + + len = (int)(s - start); + if (len <= 0) + continue; + + if (len > 255) + len = 255; + + if (p + 1 + len >= buf + 0x138) + break; + + *p++ = (uint8)len; + memcpy(p, start, len); + p += len; + count++; + } + + *countp = (uint8)count; + + used = (UI)(p - buf); + c32_put_word_lh(buf + 0x13c, used); + return(used); +} + + /* * Current verified Client32 path for mars-nwe DOS utilities: * @@ -307,3 +386,97 @@ int c32_ncp87_modify_dos_attributes(char *name, } +int c32_ncp87_get_effective_rights(const char *path_name, + uint16 dir_handle, + uint16 *rights_out, + uint16 *actual_out, + uint16 *handle_lo_out, + uint16 *handle_hi_out) +{ + uint16 handle_lo, handle_hi; + uint8 hdr[16]; + uint8 path[0x140]; + uint8 rep0[0x20]; + uint8 rep1[0x20]; + uint8 rawout[32]; + uint16 raw_ret_ax, raw_ret_dx; + uint16 actual_lo; + uint16 rights0; + uint16 rights4; + UI path_len; + int rc; + + if (!rights_out) + return(1); + + *rights_out = 0; + if (actual_out) *actual_out = 0; + if (handle_lo_out) *handle_lo_out = 0; + if (handle_hi_out) *handle_hi_out = 0; + + rc = c32_get_ncp_handle(&handle_lo, &handle_hi); + if (rc) + return(10 + rc); + + /* + * NCP 87 subfunction 29: Get effective rights. + * + * This mirrors ncpfs ncp_get_eff_directory_rights(): + * byte 29 + * byte source namespace + * byte target namespace + * word search attributes, little endian + * dword reserved, zero + * handle/path + * + * Reply is a little-endian word with NCP rights bits. + */ + memset(hdr, 0, sizeof(hdr)); + hdr[0] = 29; + hdr[1] = 0; /* source namespace DOS */ + hdr[2] = 0; /* target namespace DOS */ + c32_put_word_lh(hdr + 3, 0x0006); /* SA_ALL */ + c32_put_dword_lh(hdr + 5, 0L); /* reserved */ + + path_len = c32_build_handle_path_from_dos_path(path, (uint8)dir_handle, + 0, 0, path_name); + + memset(rep0, 0, sizeof(rep0)); + memset(rep1, 0, sizeof(rep1)); + memset(rawout, 0, sizeof(rawout)); + + C32_NCP87_Raw5_Probe(handle_lo, handle_hi, + hdr, 9, + path, path_len, + rep0, sizeof(rep0), + rep1, sizeof(rep1), + rawout); + + raw_ret_ax = c32_get_word_lh(rawout + 14); + raw_ret_dx = c32_get_word_lh(rawout + 16); + actual_lo = c32_get_word_lh(rawout + 18); + + if (actual_out) *actual_out = actual_lo; + if (handle_lo_out) *handle_lo_out = handle_lo; + if (handle_hi_out) *handle_hi_out = handle_hi; + + if (raw_ret_ax != 0 || raw_ret_dx != 0) + return(20); + + rights0 = c32_get_word_lh(rep0 + 0); + rights4 = c32_get_word_lh(rep0 + 4); + + /* + * Most NCP replies start at REP0+0. The existing RIM_ATTRIBUTES helper + * found attributes at REP0+4 on Client32. Accept the +4 location only + * if +0 is empty, so restricted rights value 0 still works when +4 is + * also zero. + */ + if (rights0 == 0 && rights4 != 0) + rights0 = rights4; + + *rights_out = rights0; + return(0); +} + + diff --git a/c32ncp.h b/c32ncp.h index 471a655..17d97d0 100644 --- a/c32ncp.h +++ b/c32ncp.h @@ -19,4 +19,11 @@ int c32_ncp87_modify_dos_attributes(char *name, uint16 *handle_lo_out, uint16 *handle_hi_out); +int c32_ncp87_get_effective_rights(const char *path, + uint16 dir_handle, + uint16 *rights_out, + uint16 *actual_out, + uint16 *handle_lo_out, + uint16 *handle_hi_out); + #endif diff --git a/flagdir.c b/flagdir.c index 1f2736c..1cb82d1 100644 --- a/flagdir.c +++ b/flagdir.c @@ -75,6 +75,61 @@ static int fd_current_dhandle(uint8 *dhandle) return(0); } + +static int fd_current_prefix(char *out, int max) +{ + uint8 connid = 0; + uint8 dhandle = 0; + uint8 flags = 0; + int drive; + char server[52]; + char path[260]; + char volume[32]; + char *p; + int i = 0; + + if (!out || max < 8) + return(-1); + + out[0] = '\0'; + + drive = fd_get_current_drive(); + if (get_drive_info((uint8)drive, &connid, &dhandle, &flags)) + return(-1); + + if (!connid || (flags & 0x80)) + return(-1); + + server[0] = '\0'; + if (get_fs_name(connid, server)) + server[0] = '\0'; + + path[0] = '\0'; + if (get_dir_path(dhandle, path) || !path[0]) + return(-1); + + p = strchr(path, ':'); + if (!p) + return(-1); + + while (path + i < p && i < (int)sizeof(volume) - 1) { + volume[i] = path[i]; + i++; + } + volume[i] = '\0'; + + if (!volume[0]) + return(-1); + + if (server[0]) + sprintf(out, "%s/%s:", server, volume); + else + sprintf(out, "%s:", volume); + + return(0); +} + + static void fd_help(void) { fprintf(stdout, "386 Usage: Flagdir [path [option...]]\n"); @@ -118,17 +173,13 @@ static void fd_display(char *path, uint32 attrs) fd_upcopy(up, path, sizeof(up)); - /* - * Novell FLAGDIR style: - * - * MARS/SYS:UDIR - * UDIR System Hidden ... - * - * For now we match the tested MARS/SYS: environment used by PUBLIC/NPUBLIC. - * The NCP logic is independent from this cosmetic header. - */ - fprintf(stdout, "MARS/SYS:%s\n", up); - fprintf(stdout, " %-10s ", up); + { + char prefix[90]; + if (fd_current_prefix(prefix, sizeof(prefix))) + prefix[0] = '\0'; + fprintf(stdout, "%s%s \n", prefix, up); + } + fprintf(stdout, " %-12.12s ", up); if (!(attrs & FD_DIR_BITS)) { fprintf(stdout, "Normal\n"); diff --git a/net.c b/net.c index 84dfe0e..03d9b29 100644 --- a/net.c +++ b/net.c @@ -36,6 +36,7 @@ static struct s_net_functions { {"LOGOUT", "logout from server", func_logout , 0}, {"FLAG", "display or modify file attributes", func_flag , 0}, {"FLAGDIR","display or modify directory attributes",func_flagdir, 0}, +{"RIGHTS", "display effective file/directory rights",func_rights, 0}, {"SLIST", "list servers", func_slist , 0}, {"PASSWD", "change password", func_passwd , 0}, #if 1 diff --git a/net.h b/net.h index ad52b22..a9e600f 100644 --- a/net.h +++ b/net.h @@ -255,6 +255,7 @@ extern int func_capture(int argc, char *argv[], int mode); /* flag.c */ extern int func_flag (int argc, char *argv[], int mode); extern int func_flagdir(int argc, char *argv[], int mode); +extern int func_rights (int argc, char *argv[], int mode); extern int ncp_17_37(uint32 last_id, uint16 objtyp, uint8 *pattern, diff --git a/rights.c b/rights.c new file mode 100644 index 0000000..d329aee --- /dev/null +++ b/rights.c @@ -0,0 +1,408 @@ +/* rights.c - Novell RIGHTS-like DOS utility, read-only v4 */ + +#include "net.h" +#include "c32ncp.h" +#include +#include +#include + +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif + +#define NW_RIGHT_S 0x01 +#define NW_RIGHT_R 0x02 +#define NW_RIGHT_W 0x04 +#define NW_RIGHT_C 0x08 +#define NW_RIGHT_E 0x10 +#define NW_RIGHT_M 0x20 +#define NW_RIGHT_F 0x40 +#define NW_RIGHT_A 0x80 + +/* NCP effective-rights bits returned by NCP87 subfunction 29. */ +#define NCP_RIGHT_READ 0x0001 +#define NCP_RIGHT_WRITE 0x0002 +#define NCP_RIGHT_OPEN 0x0004 +#define NCP_RIGHT_CREATE 0x0008 +#define NCP_RIGHT_DELETE 0x0010 +#define NCP_RIGHT_OWNER 0x0020 +#define NCP_RIGHT_SEARCH 0x0040 +#define NCP_RIGHT_MODIFY 0x0080 +#define NCP_RIGHT_SUPER 0x0100 + +static uint8 rights_map_ncp_mask(uint16 ncp_rights); + + +static int rights_same(char *a, char *b) +{ + while (*a || *b) { + int ca = *a++; + int cb = *b++; + if (ca >= 'a' && ca <= 'z') ca -= 32; + if (cb >= 'a' && cb <= 'z') cb -= 32; + if (ca != cb) return(0); + } + return(1); +} + +static int rights_is_help(char *s) +{ + if (!s) return(0); + return(rights_same(s, "/?") || rights_same(s, "-?") || rights_same(s, "?")); +} + +static void rights_usage(void) +{ + fprintf(stdout, "Usage: RIGHTS [path]\n\n"); + fprintf(stdout, "Rights = All | Supervisor | Read | Write | Create | Erase\n"); + fprintf(stdout, " Modify | File scan | Access Control\n"); +} + +static int rights_get_current_drive(void) +{ + REGS regs; + + regs.h.ah = 0x19; + int86(0x21, ®s, ®s); + return((int)regs.h.al); +} + +static int rights_current_dhandle(uint8 *connid, uint8 *dhandle) +{ + uint8 flags = 0; + int drive = rights_get_current_drive(); + + if (get_drive_info((uint8)drive, connid, dhandle, &flags)) + return(-1); + + if (!*connid || (flags & 0x80)) + return(-1); + + return(0); +} + +static int rights_is_current_path(char *path) +{ + if (!path || !*path) return(1); + if (rights_same(path, ".")) return(1); + if (rights_same(path, ".\\")) return(1); + if (rights_same(path, "./")) return(1); + return(0); +} + +static void rights_upcopy(char *dst, char *src, int max) +{ + int i = 0; + + if (!src) src = ""; + + while (*src && i < max - 1) { + char c = *src++; + if (c == '/') c = '\\'; + if (c >= 'a' && c <= 'z') c -= 32; + dst[i++] = c; + } + dst[i] = 0; +} + +static void rights_parent_path(char *dst, char *src, int max) +{ + char tmp[260]; + char *p; + + rights_upcopy(tmp, src, sizeof(tmp)); + + p = strrchr(tmp, '\\'); + if (!p) p = strrchr(tmp, ':'); + + if (!p) { + dst[0] = '\0'; + return; + } + + if (*p == ':') { + p++; + *p = '\0'; + } else { + *p = '\0'; + } + + strmaxcpy(dst, tmp, max - 1); +} + +static int rights_path_is_dir(char *path) +{ + struct stat st; + + if (rights_is_current_path(path)) + return(1); + + if (stat(path, &st) == 0) { + if (st.st_mode & S_IFDIR) + return(1); + } + + return(0); +} + + +static int rights_current_prefix(char *out, int max) +{ + uint8 connid = 0; + uint8 dhandle = 0; + uint8 flags = 0; + int drive; + char server[52]; + char dpath[260]; + char volume[32]; + char *p; + int i = 0; + + if (!out || max < 8) + return(-1); + + out[0] = '\0'; + + drive = rights_get_current_drive(); + if (get_drive_info((uint8)drive, &connid, &dhandle, &flags)) + return(-1); + + if (!connid || (flags & 0x80)) + return(-1); + + server[0] = '\0'; + if (get_fs_name(connid, server)) + server[0] = '\0'; + + dpath[0] = '\0'; + if (get_dir_path(dhandle, dpath) || !dpath[0]) + return(-1); + + p = strchr(dpath, ':'); + if (!p) + return(-1); + + while (dpath + i < p && i < (int)sizeof(volume) - 1) { + volume[i] = dpath[i]; + i++; + } + volume[i] = '\0'; + + if (!volume[0]) + return(-1); + + if (server[0]) + sprintf(out, "%s/%s:", server, volume); + else + sprintf(out, "%s:", volume); + + return(0); +} + + +static void rights_make_header_path(char *out, char *path) +{ + char up[260]; + char prefix[90]; + + if (rights_current_prefix(prefix, sizeof(prefix))) + prefix[0] = '\0'; + + if (rights_is_current_path(path)) { + strcpy(out, prefix); + return; + } + + rights_upcopy(up, path, sizeof(up)); + strcpy(out, prefix); + strcat(out, up); +} + +static int rights_effective_mask(char *path, int is_dir, uint8 *mask) +{ + uint8 connid = 0; + uint8 dhandle = 0; + uint8 eff = 0; + char usepath[260]; + int newhandle; + uint16 ncp_rights = 0; + + if (mask) *mask = 0; + + if (rights_current_dhandle(&connid, &dhandle)) + return(-1); + + /* + * Prefer the explicit Client32 NCP87 effective-rights request. This is + * the DOS Client32 equivalent of ncpfs ncp_get_eff_directory_rights(). + * It works for both files and directories, so pass the requested path + * itself instead of mapping files to their parent directory. + */ + if (!c32_ncp87_get_effective_rights(rights_is_current_path(path) ? "" : path, + (uint16)dhandle, + &ncp_rights, + NULL, NULL, NULL)) { + if (mask) *mask = rights_map_ncp_mask(ncp_rights); + return(0); + } + + /* + * Fallback for older requesters/paths: allocate a temporary directory + * handle and use the returned old-style effective-rights byte. + * This cannot directly target a file, so files are mapped to their parent. + */ + if (rights_is_current_path(path)) { + usepath[0] = '\0'; + } else if (is_dir) { + rights_upcopy(usepath, path, sizeof(usepath)); + } else { + rights_parent_path(usepath, path, sizeof(usepath)); + } + + newhandle = alloc_temp_dir_handle(dhandle, usepath, 0, &eff); + if (newhandle < 0) { + if (usepath[0]) { + int subdir = 1; + int r = ncp_16_02(dhandle, (uint8 *)usepath, &subdir, + NULL, NULL, NULL); + if (r >= 0) { + eff = (uint8)r; + } else { + return(-1); + } + } else { + eff = 0xff; + } + } else { + dealloc_dir_handle(newhandle); + } + + if (mask) *mask = eff; + return(0); +} + + +static uint8 rights_map_ncp_mask(uint16 ncp_rights) +{ + uint8 mask = 0; + + if (ncp_rights & NCP_RIGHT_SUPER) mask |= NW_RIGHT_S; + if (ncp_rights & NCP_RIGHT_READ) mask |= NW_RIGHT_R; + if (ncp_rights & NCP_RIGHT_WRITE) mask |= NW_RIGHT_W; + if (ncp_rights & NCP_RIGHT_CREATE) mask |= NW_RIGHT_C; + if (ncp_rights & NCP_RIGHT_DELETE) mask |= NW_RIGHT_E; + if (ncp_rights & NCP_RIGHT_MODIFY) mask |= NW_RIGHT_M; + if (ncp_rights & NCP_RIGHT_SEARCH) mask |= NW_RIGHT_F; + if (ncp_rights & NCP_RIGHT_OWNER) mask |= NW_RIGHT_A; + + return(mask); +} + + +static void rights_mask_string(uint8 mask, char *out) +{ + out[0] = (mask & NW_RIGHT_S) ? 'S' : '-'; + out[1] = (mask & NW_RIGHT_R) ? 'R' : '-'; + out[2] = (mask & NW_RIGHT_W) ? 'W' : '-'; + out[3] = (mask & NW_RIGHT_C) ? 'C' : '-'; + out[4] = (mask & NW_RIGHT_E) ? 'E' : '-'; + out[5] = (mask & NW_RIGHT_M) ? 'M' : '-'; + out[6] = (mask & NW_RIGHT_F) ? 'F' : '-'; + out[7] = (mask & NW_RIGHT_A) ? 'A' : '-'; + out[8] = '\0'; +} + +/* + * Novell RIGHTS layout: + * - lines with star: two leading blanks, star, blank, text + * - lines without star: four leading blanks, text + * Keep the right-column letter at a fixed-ish DOS-screen position. + */ +static void rights_print_line(int have, int star, char *text, char letter) +{ + (void)have; + + if (star) + fprintf(stdout, " * %-43s(%c)\n", text, letter); + else + fprintf(stdout, " %-43s(%c)\n", text, letter); +} + +static void rights_display(char *path, int is_dir, uint8 mask) +{ + char hdr[300]; + char mstr[10]; + + rights_make_header_path(hdr, path); + rights_mask_string(mask, mstr); + + fprintf(stdout, "%s\n", hdr); + + if (is_dir) + fprintf(stdout, "Your Effective Rights for this directory are [%s]\n", mstr); + else + fprintf(stdout, "Your Effective Rights for this file are [%s]\n", mstr); + + if (is_dir) { + rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to Directory.", 'S'); + rights_print_line(mask & NW_RIGHT_R, 1, "May Read from File.", 'R'); + rights_print_line(mask & NW_RIGHT_W, 1, "May Write to File.", 'W'); + rights_print_line(mask & NW_RIGHT_C, 0, "May Create Subdirectories and Files.", 'C'); + rights_print_line(mask & NW_RIGHT_E, 0, "May Erase Directory.", 'E'); + rights_print_line(mask & NW_RIGHT_M, 0, "May Modify Directory.", 'M'); + rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for Files.", 'F'); + rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A'); + + fprintf(stdout, "\n"); + fprintf(stdout, " * Has no effect on directory.\n\n"); + fprintf(stdout, " Entries in Directory May Inherit [%s] rights.\n", mstr); + if (mask == 0xff) + fprintf(stdout, " You have ALL RIGHTS to Directory Entry.\n"); + } else { + rights_print_line(mask & NW_RIGHT_S, 0, "You have Supervisor Rights to File.", 'S'); + rights_print_line(mask & NW_RIGHT_R, 0, "May Read from File.", 'R'); + rights_print_line(mask & NW_RIGHT_W, 0, "May Write to File.", 'W'); + rights_print_line(mask & NW_RIGHT_C, 1, "May Create Subdirectories and Files.", 'C'); + rights_print_line(mask & NW_RIGHT_E, 0, "May Erase File.", 'E'); + rights_print_line(mask & NW_RIGHT_M, 0, "May Modify File.", 'M'); + rights_print_line(mask & NW_RIGHT_F, 0, "May Scan for File.", 'F'); + rights_print_line(mask & NW_RIGHT_A, 0, "May Change Access Control.", 'A'); + + fprintf(stdout, "\n"); + fprintf(stdout, " * Create is necessary to salvage a file that has been deleted.\n\n"); + if (mask == 0xff) + fprintf(stdout, " You have ALL RIGHTS to Directory Entry.\n"); + } +} + +int func_rights(int argc, char *argv[], int mode) +{ + char *path = "."; + uint8 mask = 0; + int is_dir; + + (void)mode; + + if (argc > 2) { + rights_usage(); + return(1); + } + + if (argc == 2) { + if (rights_is_help(argv[1])) { + rights_usage(); + return(0); + } + path = argv[1]; + } + + is_dir = rights_path_is_dir(path); + + if (rights_effective_mask(path, is_dir, &mask)) { + fprintf(stdout, "Specified path not locatable.\n"); + return(1); + } + + rights_display(path, is_dir, mask); + return(0); +} diff --git a/teste.c b/teste.c index c6fcc99..e0323c6 100644 --- a/teste.c +++ b/teste.c @@ -22,11 +22,11 @@ int main() close(fd); _chmod(fn, 1, _chmod(fn, 0) | 0x80 ); stat(fn, &stbuff); - printf("Filesize ber stat =%ld\n", stbuff.st_size); + printf("Filesize �ber stat =%ld\n", stbuff.st_size); fd = open(fn, O_RDWR | O_BINARY |O_DENYNONE); offset = lseek(fd, 0L, SEEK_END); - printf("Filesize ber lseek =%ld\n", offset); + printf("Filesize �ber lseek =%ld\n", offset); write(fd, buff, strlen(buff)); lseek(fd, 0L, SEEK_SET);