Found out that there is no need in bypassing "adduser", as this command works just fine: % adduser --allow-all-names --home /root --shell /usr/bin/zsh Плохой So it is actually possible to create user with unicode username in a correct way (at least on Linux). Now about the patch: % watch=(all); log root has logged on /proc/10045/fd/2 from . Студент has logged on pts/29 from 127.0.0.1. % watch=(notme); log # Broken root has logged on /proc/10045/fd/2 from . Студент has logged on pts/29 from 127.0.0.1. % watch=(Студент); log Студент has logged on pts/29 from 127.0.0.1. Also tested "too long" username: % echo ${#USERNAME} 33 % watch=(all); log root has logged on /proc/10045/fd/2 from . oooooooooooooooooooooooooooooooo has logged on pts/29 from 127.0.0.1. % watch=(notme); log root has logged on /proc/10045/fd/2 from . % watch=(${(l:33::o:):-}); log % watch=(${(l:32::o:):-}); log oooooooooooooooooooooooooooooooo has logged on pts/29 from . And no, Cygwin usernames are case-sensitive. Sent via Gmail сб, 7 окт. 2023 г., 05:05 Oliver Kiddle : > Максим wrote: > > Hello again. I found another bug with cyrillic usernames in zsh (again on > > Cygwin, but can be reproduced on Linux) > > Reproducing does involve bypassing utilities like useradd which complain > about invalid usernames. But I can imagine such rules will increasingly > be relaxed and there's no reason for zsh to make assumptions. > > > % watch=(Студент); log # ("Студент" record is missing) > > The value from $watch is metafied and that's what patcompile() and > pattry() need so the fix below uses metafy() on the username from > utmp. > > However, in looking closer at the code I observed the existing use of > sizeof(u->ut_name) which is 32 on my system. So I tried creating 32 and > 33 character usernames (which, incidentally, useradd was happy with) and > as I suspected u->ut_name is not null-terminated for these. So the patch > uses strnlen() with the sizeof() for n to get the length to pass > to metafy(). We have no existing uses of strnlen() but I don't foresee > portability issues given that it is attributed to the 2008 POSIX > standard and is supported in Solaris 10 which is from a few years prior > to that. If needed, it'd be easy to provide an alternative > implementation. > > To match the 33 character username, it does need to be truncated in > $watch. last -w does manage to print the full username, would be good > to know how. For the hostname, our code was using strlen() rather than > sizeof(). I can't see why this would be needed. I would have tried > putting UTF-8 in my hosts file to test that that but I'm only getting IP > addresses in utmp. I guess we could do reverse lookups but it hardly > seems worth it for the amount of use watch/log likely get these days. > > The example also uses an uppercase letter. Usernames on Unix are > case-sensitive but it wouldn't surprise me if they aren't on Cygwin. > If so, should we add #ifdefs for that? > > Oliver > > diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c > index 0de8cbf9a..2ad962fb6 100644 > --- a/Src/Modules/watch.c > +++ b/Src/Modules/watch.c > @@ -423,20 +423,22 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char > *fmt, int prnt, int fini) > /* See if the watch entry matches */ > > static int > -watchlog_match(char *teststr, char *actual, int len) > +watchlog_match(char *teststr, char *actual, size_t buflen) > { > int ret = 0; > Patprog pprog; > char *str = dupstring(teststr); > + int len = strnlen(actual, buflen); > + char *user = metafy(actual, len, META_USEHEAP); > > tokenize(str); > > if ((pprog = patcompile(str, PAT_STATIC, 0))) { > queue_signals(); > - if (pattry(pprog, actual)) > + if (pattry(pprog, user)) > ret = 1; > unqueue_signals(); > - } else if (!strncmp(actual, teststr, len)) > + } else if (!strcmp(user, teststr)) > ret = 1; > return ret; > } > @@ -488,7 +490,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, > char *fmt) > for (vv = ++v; *vv && *vv != '%'; vv++); > sav = *vv; > *vv = '\0'; > - if (!watchlog_match(v, u->ut_host, strlen(v))) > + if (!watchlog_match(v, u->ut_host, sizeof(u->ut_host))) > bad = 1; > *vv = sav; > v = vv; >