BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) { pstring passwordprogram; pstring chatsequence; size_t i; size_t len; strlower(name); DEBUG(3,("Password change for user: %s\n",name)); #if DEBUG_PASSWORD DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); #endif /* Take the passed information and test it for minimum criteria */ /* Minimum password length */ if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */ { DEBUG(0,("Password Change: user %s, New password is shorter than minimum password length = %d\n", name, lp_min_passwd_length())); return (False); /* inform the user */ } /* Password is same as old password */ if (strcmp(oldpass,newpass) == 0) /* don't allow same password */ { DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */ return (False); /* inform the user */ } pstrcpy(passwordprogram,lp_passwd_program()); pstrcpy(chatsequence,lp_passwd_chat()); if (!*chatsequence) { DEBUG(2,("Null chat sequence - no password changing\n")); return(False); } if (!*passwordprogram) { DEBUG(2,("Null password program - no password changing\n")); return(False); } /* * Check the old and new passwords don't contain any control * characters. */ len = strlen(oldpass); for(i = 0; i < len; i++) { if (iscntrl((int)oldpass[i])) { DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n")); return False; } } len = strlen(newpass); for(i = 0; i < len; i++) { if (iscntrl((int)newpass[i])) { DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n")); return False; } } pstring_sub(passwordprogram,"%u",name); /* note that we do NOT substitute the %o and %n in the password program as this would open up a security hole where the user could use a new password containing shell escape characters */ pstring_sub(chatsequence,"%u",name); all_string_sub(chatsequence,"%o",oldpass,sizeof(pstring)); all_string_sub(chatsequence,"%n",newpass,sizeof(pstring)); return(chat_with_program(passwordprogram,name,chatsequence, as_root)); }
static BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence, BOOL as_root) { char *slavedev; int master; pid_t pid, wpid; int wstat; BOOL chstat = False; /* allocate a pseudo-terminal device */ if ((master = findpty (&slavedev)) < 0) { DEBUG(3,("Cannot Allocate pty for password change: %s\n",name)); return(False); } /* * We need to temporarily stop CatchChild from eating * SIGCLD signals as it also eats the exit status code. JRA. */ CatchChildLeaveStatus(); #ifdef __uClinux__ /* Hmmm, need to check this one further... */ DEBUG(0,("%s(%d): vfork()ing\n",__FILE__,__LINE__)); if ((pid = vfork()) < 0) { #else if ((pid = fork()) < 0) { #endif DEBUG(3,("Cannot fork() child for password change: %s\n",name)); close(master); CatchChild(); return(False); } /* we now have a pty */ if (pid > 0){ /* This is the parent process */ if ((chstat = talktochild(master, chatsequence)) == False) { DEBUG(3,("Child failed to change password: %s\n",name)); kill(pid, SIGKILL); /* be sure to end this process */ } while((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { if(errno == EINTR) { errno = 0; continue; } break; } if (wpid < 0) { DEBUG(3,("The process is no longer waiting!\n\n")); close(master); CatchChild(); return(False); } /* * Go back to ignoring children. */ CatchChild(); close(master); if (pid != wpid) { DEBUG(3,("We were waiting for the wrong process ID\n")); return(False); } if (WIFEXITED(wstat) == 0) { DEBUG(3,("The process exited while we were waiting\n")); return(False); } if (WEXITSTATUS(wstat) != 0) { DEBUG(3,("The status of the process exiting was %d\n", wstat)); return(False); } } else { /* CHILD */ /* * Lose any oplock capabilities. */ set_process_capability(KERNEL_OPLOCK_CAPABILITY, False); set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY, False); /* make sure it doesn't freeze */ alarm(20); if (as_root) become_root(False); DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,(int)getuid(),(int)getgid())); chstat = dochild(master, slavedev, name, passwordprogram, as_root); /* * The child should never return from dochild() .... */ DEBUG(0,("chat_with_program: Error: dochild() returned %d\n", chstat )); exit(1); } if (chstat) DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name)); return (chstat); } BOOL chgpasswd(char *name,char *oldpass,char *newpass, BOOL as_root) { pstring passwordprogram; pstring chatsequence; size_t i; size_t len; strlower(name); DEBUG(3,("Password change for user: %s\n",name)); #if DEBUG_PASSWORD DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); #endif /* Take the passed information and test it for minimum criteria */ /* Minimum password length */ if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */ { DEBUG(0,("Password Change: user %s, New password is shorter than minimum password length = %d\n", name, lp_min_passwd_length())); return (False); /* inform the user */ } /* Password is same as old password */ if (strcmp(oldpass,newpass) == 0) /* don't allow same password */ { DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */ return (False); /* inform the user */ } pstrcpy(passwordprogram,lp_passwd_program()); pstrcpy(chatsequence,lp_passwd_chat()); if (!*chatsequence) { DEBUG(2,("Null chat sequence - no password changing\n")); return(False); } if (!*passwordprogram) { DEBUG(2,("Null password program - no password changing\n")); return(False); } /* * Check the old and new passwords don't contain any control * characters. */ len = strlen(oldpass); for(i = 0; i < len; i++) { if (iscntrl((int)oldpass[i])) { DEBUG(0,("chat_with_program: oldpass contains control characters (disallowed).\n")); return False; } } len = strlen(newpass); for(i = 0; i < len; i++) { if (iscntrl((int)newpass[i])) { DEBUG(0,("chat_with_program: newpass contains control characters (disallowed).\n")); return False; } } pstring_sub(passwordprogram,"%u",name); /* note that we do NOT substitute the %o and %n in the password program as this would open up a security hole where the user could use a new password containing shell escape characters */ pstring_sub(chatsequence,"%u",name); all_string_sub(chatsequence,"%o",oldpass,sizeof(pstring)); all_string_sub(chatsequence,"%n",newpass,sizeof(pstring)); return(chat_with_program(passwordprogram,name,chatsequence, as_root)); }
static int do_global_checks(void) { int ret = 0; SMB_STRUCT_STAT st; const char *socket_options; if (lp_security() >= SEC_DOMAIN && !lp_encrypt_passwords()) { fprintf(stderr, "ERROR: in 'security=domain' mode the " "'encrypt passwords' parameter must always be " "set to 'true'.\n\n"); ret = 1; } if (lp_we_are_a_wins_server() && lp_wins_server_list()) { fprintf(stderr, "ERROR: both 'wins support = true' and " "'wins server = <server list>' cannot be set in " "the smb.conf file. nmbd will abort with this " "setting.\n\n"); ret = 1; } if (strequal(lp_workgroup(), lp_netbios_name())) { fprintf(stderr, "WARNING: 'workgroup' and 'netbios name' " "must differ.\n\n"); } if (!directory_exist_stat(lp_lock_directory(), &st)) { fprintf(stderr, "ERROR: lock directory %s does not exist\n\n", lp_lock_directory()); ret = 1; } else if ((st.st_ex_mode & 0777) != 0755) { fprintf(stderr, "WARNING: lock directory %s should have " "permissions 0755 for browsing to work\n\n", lp_lock_directory()); } if (!directory_exist_stat(lp_state_directory(), &st)) { fprintf(stderr, "ERROR: state directory %s does not exist\n\n", lp_state_directory()); ret = 1; } else if ((st.st_ex_mode & 0777) != 0755) { fprintf(stderr, "WARNING: state directory %s should have " "permissions 0755 for browsing to work\n\n", lp_state_directory()); } if (!directory_exist_stat(lp_cache_directory(), &st)) { fprintf(stderr, "ERROR: cache directory %s does not exist\n\n", lp_cache_directory()); ret = 1; } else if ((st.st_ex_mode & 0777) != 0755) { fprintf(stderr, "WARNING: cache directory %s should have " "permissions 0755 for browsing to work\n\n", lp_cache_directory()); } if (!directory_exist_stat(lp_pid_directory(), &st)) { fprintf(stderr, "ERROR: pid directory %s does not exist\n\n", lp_pid_directory()); ret = 1; } if (lp_passdb_expand_explicit()) { fprintf(stderr, "WARNING: passdb expand explicit = yes is " "deprecated\n\n"); } /* * Socket options. */ socket_options = lp_socket_options(); if (socket_options != NULL && (strstr(socket_options, "SO_SNDBUF") || strstr(socket_options, "SO_RCVBUF") || strstr(socket_options, "SO_SNDLOWAT") || strstr(socket_options, "SO_RCVLOWAT"))) { fprintf(stderr, "WARNING: socket options = %s\n" "This warning is printed because you set one of the\n" "following options: SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT,\n" "SO_RCVLOWAT\n" "Modern server operating systems are tuned for\n" "high network performance in the majority of situations;\n" "when you set 'socket options' you are overriding those\n" "settings.\n" "Linux in particular has an auto-tuning mechanism for\n" "buffer sizes (SO_SNDBUF, SO_RCVBUF) that will be\n" "disabled if you specify a socket buffer size. This can\n" "potentially cripple your TCP/IP stack.\n\n" "Getting the 'socket options' correct can make a big\n" "difference to your performance, but getting them wrong\n" "can degrade it by just as much. As with any other low\n" "level setting, if you must make changes to it, make\n " "small changes and test the effect before making any\n" "large changes.\n\n", socket_options); } /* * Password server sanity checks. */ if((lp_security() >= SEC_DOMAIN) && !*lp_password_server()) { const char *sec_setting; if(lp_security() == SEC_DOMAIN) sec_setting = "domain"; else if(lp_security() == SEC_ADS) sec_setting = "ads"; else sec_setting = ""; fprintf(stderr, "ERROR: The setting 'security=%s' requires the " "'password server' parameter be set to the " "default value * or a valid password server.\n\n", sec_setting ); ret = 1; } if((lp_security() >= SEC_DOMAIN) && (strcmp(lp_password_server(), "*") != 0)) { const char *sec_setting; if(lp_security() == SEC_DOMAIN) sec_setting = "domain"; else if(lp_security() == SEC_ADS) sec_setting = "ads"; else sec_setting = ""; fprintf(stderr, "WARNING: The setting 'security=%s' should NOT " "be combined with the 'password server' " "parameter.\n" "(by default Samba will discover the correct DC " "to contact automatically).\n\n", sec_setting ); } /* * Password chat sanity checks. */ if(lp_security() == SEC_USER && lp_unix_password_sync()) { /* * Check that we have a valid lp_passwd_program() if not using pam. */ #ifdef WITH_PAM if (!lp_pam_password_change()) { #endif if((lp_passwd_program(talloc_tos()) == NULL) || (strlen(lp_passwd_program(talloc_tos())) == 0)) { fprintf(stderr, "ERROR: the 'unix password sync' " "parameter is set and there is no valid " "'passwd program' parameter.\n\n"); ret = 1; } else { const char *passwd_prog; char *truncated_prog = NULL; const char *p; passwd_prog = lp_passwd_program(talloc_tos()); p = passwd_prog; next_token_talloc(talloc_tos(), &p, &truncated_prog, NULL); if (truncated_prog && access(truncated_prog, F_OK) == -1) { fprintf(stderr, "ERROR: the 'unix password sync' " "parameter is set and the " "'passwd program' (%s) cannot be " "executed (error was %s).\n\n", truncated_prog, strerror(errno)); ret = 1; } } #ifdef WITH_PAM } #endif if(lp_passwd_chat(talloc_tos()) == NULL) { fprintf(stderr, "ERROR: the 'unix password sync' parameter is " "set and there is no valid 'passwd chat' " "parameter.\n\n"); ret = 1; } if ((lp_passwd_program(talloc_tos()) != NULL) && (strlen(lp_passwd_program(talloc_tos())) > 0)) { /* check if there's a %u parameter present */ if(strstr_m(lp_passwd_program(talloc_tos()), "%u") == NULL) { fprintf(stderr, "ERROR: the 'passwd program' (%s) " "requires a '%%u' parameter.\n\n", lp_passwd_program(talloc_tos())); ret = 1; } } /* * Check that we have a valid script and that it hasn't * been written to expect the old password. */ if(lp_encrypt_passwords()) { if(strstr_m( lp_passwd_chat(talloc_tos()), "%o")!=NULL) { fprintf(stderr, "ERROR: the 'passwd chat' script [%s] " "expects to use the old plaintext " "password via the %%o substitution. With " "encrypted passwords this is not " "possible.\n\n", lp_passwd_chat(talloc_tos()) ); ret = 1; } } } if (strlen(lp_winbind_separator()) != 1) { fprintf(stderr, "ERROR: the 'winbind separator' parameter must " "be a single character.\n\n"); ret = 1; } if (*lp_winbind_separator() == '+') { fprintf(stderr, "'winbind separator = +' might cause problems " "with group membership.\n\n"); } if (lp_algorithmic_rid_base() < BASE_RID) { /* Try to prevent admin foot-shooting, we can't put algorithmic rids below 1000, that's the 'well known RIDs' on NT */ fprintf(stderr, "'algorithmic rid base' must be equal to or " "above %lu\n\n", BASE_RID); } if (lp_algorithmic_rid_base() & 1) { fprintf(stderr, "'algorithmic rid base' must be even.\n\n"); } #ifndef HAVE_DLOPEN if (lp_preload_modules()) { fprintf(stderr, "WARNING: 'preload modules = ' set while loading " "plugins not supported.\n\n"); } #endif if (!lp_passdb_backend()) { fprintf(stderr, "ERROR: passdb backend must have a value or be " "left out\n\n"); } if (lp_os_level() > 255) { fprintf(stderr, "WARNING: Maximum value for 'os level' is " "255!\n\n"); } if (strequal(lp_dos_charset(), "UTF8") || strequal(lp_dos_charset(), "UTF-8")) { fprintf(stderr, "ERROR: 'dos charset' must not be UTF8\n\n"); ret = 1; } return ret; }