// Custom Ident generation - Adam Prato - 2/11/03 // // This is a hack I made to netbsd's identd service to allow users to customize // their ident's (really only useful for irc, but whatever, served as a good // C refresher). // // Unfortunately, I found out after this was written that FreeBSD has most of // this functionality, minus the dictid feature I think. A port of FreeBSD's // identd might have been a better idea. // // A user can control what identd returns by modifying $HOME/.identd in one // of the following ways: // 1) create a list of identd entries, one per line. // 2) Specify specific return string for ident by setting the first line to // =STRING // Note: the strings randalpha and dictid are already taken. // 3) Setting the first line in .identd to '=randalpha' will create an 8 // character random lowercase alphabetic string // 4) Setting the first line in .identd to 'dictid' will choose a random word // from DICTIONARY (/usr/share/dict/web2a) // // Here are the diffs to Makefile and parse.c // // $ diff -u Makefile.old Makefile // --- Makefile.old Tue Feb 11 21:26:49 2003 // +++ Makefile Tue Feb 11 21:39:46 2003 // @@ -10,6 +10,6 @@ // LDADD+= -lkvm // DPADD+= ${LIBKVM} // // -SRCS= netbsd.c version.c proxy.c config.c parse.c identd.c // +SRCS= custom_id.c netbsd.c version.c proxy.c config.c parse.c identd.c // // .include // // $ diff -u parse.c.old parse.c // --- parse.c.old Tue Feb 11 21:23:56 2003 // +++ parse.c Tue Feb 11 21:25:11 2003 // @@ -53,6 +53,8 @@ // static int check_noident __P((const char *)); // static int valid_fhost(struct in_addr *, char *); // // +extern char *custom_id(struct passwd *); // + // /* // ** This function will eat whitespace characters until // ** either a non-whitespace character is read, or EOF // @@ -714,7 +716,7 @@ // other_flag ? "OTHER" : "UNIX", // charset_name ? " , " : "", // charset_name ? charset_name : "", // - pwp->pw_name); // + custom_id(pwp)); // // } while(fflush(stdout), fflush(stderr), multi_flag && eat_whitespace()); // #include #include #include #include #include #include #include #include #include #include #include #include "identd.h" #include "error.h" #define LSIZE 80 #define MAX_USER 10 #define DICTIONARY "/usr/share/dict/web2a" extern int errno; char ruser[MAX_USER]; off_t getrand (void) { off_t rs; int rd; /* use random() if we cant open random device */ if((rd = open("/dev/urandom", O_RDONLY)) < 0) { srandom(getpid()); rs=random(); } else { /* use random() if we cant read random data from device */ if (read(rd, &rs, sizeof(rs)) < -1) { srandom(getpid()); rs=random(); } } close(rd); /* dont want a negative offset */ rs = llabs(rs); return(rs); } int randalpha(ruser) char *ruser; { char buf[9]; off_t rs; long r; int i; char x; char *alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; /* get a real random number, and seed the prng */ rs=getrand(); srandom(rs); for(i=0,r=random();i<8;i++,r=random()) { /* use modulus to pick a random character from array */ x=(abs((int) r % strlen(alphanum))); buf[i] = alphanum[x]; } buf[i] = '\0'; if (strlen(buf) > 0 ) { strncpy(ruser,buf,MAX_USER); return(0); } else return(-1); } int dictid(fp, fd, ruser) FILE *fp; int fd; char *ruser; { int ret = 0; char *noid = "nodictid"; char *id; struct stat st; off_t fsz; off_t fpos; char *line; char *sp; int len; int back = 2; int c = 0; /* set ruser and noid to default */ id = noid; strncpy(ruser, noid, MAX_USER); /* find the size of the file so we can pick a random position within */ if(fstat(fd, &st) < 0) { syslog(LOG_DEBUG, "Error(%d) stating file: %s\n", strerror(errno)); return(-1); } fsz = st.st_size; /* we dont want to pick the EOF, and limit the number of tries (gjvc++) */ while ( ((fpos = getrand() % fsz) == fsz) || (++c < MAX_USER )); /* pick random position in file, probably mid-line */ if(fseek(fp, fpos, SEEK_SET) >= 0 ) { /* eat up remainder of current line, except if at beginning of file */ ftello(fp) && (line=fgetln(fp, &len)); /* backoff if we are at EOF */ while (feof(fp) || (ftello(fp) == fsz)) { fpos = fsz / (off_t) back; if ( (fseek(fp, fpos, SEEK_SET) == -1) || !(ftello(fp) && (line=fgetln(fp, &len))) ) id = noid; back *= 2; } /* keep reading lines until we get a line thats short enough or eof */ while ( ((line = fgetln(fp, &len)) != NULL) && (len > 0) && (len > MAX_USER) ); /* get line, null terminate it */ if (line[len - 1] == '\n') { line[len - 1] = '\0'; id = line; } /* make room for null */ else { char *lbuf; if ((lbuf = (char *)malloc(len + 1)) == NULL) { syslog(LOG_DEBUG,"malloc() error!\n"); id = noid; } memcpy(lbuf, line, len); lbuf[len] = '\0'; strncat(lbuf, lbuf, MAX_USER); id = lbuf; } } /* if line was set to null (IE when we hit eof) set it to something */ if(!strlen(id)) id = noid; /* convert spaces to underscores */ while(sp=index(id,' ')) { *sp = '_'; } strncpy(ruser, id, MAX_USER); return(ret); } char *custom_id(pw) struct passwd *pw; { char *user; int uid; char custfile[FILENAME_MAX]; struct stat st; int fd; FILE *fp; char *line; size_t len; char *cp; char *re = "[^a-zA-Z0-9_-]"; regex_t preg; size_t nmatch; regmatch_t pmatch; struct passwd *rpw; /* get identd configuration for valid user */ if (!pw) return "nonexistant_user"; user = (char *)malloc(strlen(pw->pw_name) +1 ); strncpy(user,pw->pw_name,strlen(pw->pw_name)); uid = pw->pw_uid; snprintf(custfile, FILENAME_MAX, "%s/.identd", pw->pw_dir); /* by default, ident is the username */ strncpy(ruser, user, MAX_USER); /* open filedescriptor to custom ident file */ if((fd = open(custfile, O_RDONLY)) > 0) { /* stat the file so we can perform some checks */ if(fstat(fd, &st) < 0) syslog(LOG_DEBUG, "Error(%d) stating file: %s\n", strerror(errno)); else { /* The user shouldnt be pointing linking to files he doesnt own */ if (!(uid == st.st_uid)) syslog(LOG_DEBUG, "File %s uid mismatch (%d != %d)\n", custfile, uid, st.st_uid); else { /* open filestream to the checked file */ if((fp = fdopen(fd, "r")) < 0) syslog(LOG_DEBUG, "Could not reopen file for reading: %s\n", strerror(errno)); else { /* check the first line for specific instructions */ line=fgetln(fp, &len); if (len > 1) { if((line[len - 1] == EOF) || (line[len-1] == '\n')) line[len-1] = '\0'; cp = line; /* if the line is '=string', we follow string's cmd */ if ((*cp == '=') && (strlen(cp) > 1)) { *cp++; /* '=dictid' means we should open dictionary and return a word from it */ if (!strncmp(cp, "dictid", strlen("dictid"))) { /* close pointers and open the dictionary */ fclose(fp); close(fd); if((fd = open(DICTIONARY, O_RDONLY)) < 0) syslog(LOG_DEBUG, "Error opening file %s: %s\n", custfile, strerror(errno)); else if ((fp = fdopen(fd, "r")) < 0) syslog(LOG_DEBUG, "Could not open file %s for reading: %s\n", strerror(errno)); else dictid(fp,fd, &ruser); } /* '=randalpha' means generate random string */ else if (!strncmp(cp, "randalpha", strlen("randalpha"))) randalpha(ruser); else /* '=string' means we set reply to 'string' */ strncpy(ruser, cp, MAX_USER); } else /* chose a random word from the .identd file */ dictid(fp,fd, &ruser); } } fclose(fp); close(fd); } } } /* make sure we are returning valid characters */ if (!regcomp(&preg,re,REG_BASIC)) if (!regexec(&preg, ruser, 1, &pmatch, 0)) { syslog(LOG_DEBUG, "ident '%s' contains illegal characters\n",ruser); strncpy(ruser, user, MAX_USER); } /* not a good idea to impersonate another user on the box */ if (rpw=getpwnam(ruser)) if (uid != rpw->pw_uid) { syslog(LOG_DEBUG,"User tried to spoof (%s != %s)\n", user, rpw->pw_name); strncpy(ruser, user, MAX_USER); } /* all done */ return(ruser); }