/* vi:set tabstop=4 nocindent noautoindent: */


/**
パスワードライブラリ


*/



#include  "password.h"




/**
char*   get_passwd(char* user_id)

	機能：ユーザ user_id のパスワードを得る．
		　パスワードの検索順は　/etc/passwd, /etc/shadow, NIS

	引数：user_id -- ユーザ名

	戻り値：ユーザの（暗号化された）パスワード
		  	NULL  失敗
*/
char*   get_passwd(char* user_id)
{
	struct passwd* pw;
	struct spwd*   sp;
	char*  pass;

	pass = (char*)malloc(LPASS+1);
	if (pass==NULL) return NULL;
	memset(pass, 0, LPASS+1);


	// for /etc/passwd
	pw = getpwnam((const char*)user_id);
	if (pw==NULL) {
		free(pass);
		return NULL;
	}
	strncpy(pass, pw->pw_passwd, LPASS);
	if (strcmp(pass, "*") && strcmp(pass, "x") && strcmp(pass, "!")) return pass;


#ifdef HAVE_GETSPNAM
	// for /etc/shadow
	sp = getspnam((const char*)user_id);
	if (sp!=NULL) {
		strncpy(pass, sp->sp_pwdp, LPASS);
		return pass;
	}
#endif

#ifdef HAVE_RPCSVC_YPCLNT_H
	// for NIS
	pw = getnisnam(user_id);
	if (pw!=NULL) {
		strncpy(pass, pw->pw_passwd, LPASS);
		free_pw(pw);
		return pass;
	}
#endif

	return NULL;
}






#ifdef HAVE_RPCSVC_YPCLNT_H

/**
char* get_nis_passwdf(char* usrid)

	機能：usrid の NIS のパスワードファイル情報を得る．

	引数：userid -- 調べるユーザ名

	戻り値：userid のパスワードファイル情報（パスワードファイルの１行）．要free
*/
char* get_nis_passwdf(char* usrid)
{
	char* inmap="passwd.byname";
	int   ret, keylen, vallen, usrlen;
	char* domainname;
	char* key;
	char* val;
	char* uname;
	char* nis;

	usrlen = strlen(usrid);
	uname = (char*)malloc(usrlen + 2);
	if (uname==NULL) return NULL;
	strncpy(uname, usrid, usrlen);
	uname[usrlen]   = ':';
	uname[usrlen+1] = '\0';

	yp_get_default_domain(&domainname);

	ret = yp_first(domainname, inmap, &key, &keylen, &val, &vallen);   
	while(ret==0){
		if (!strncmp(val, uname, usrlen+1)) {
			free(uname);
			nis = (char*)malloc(strlen(val)+1);
			if (nis==NULL) return NULL;
			strncpy(nis, val, strlen(val)+1);
			return nis;
		}
		ret = yp_next(domainname, inmap, key, keylen, &key, &keylen, &val, &vallen);   
	}

	free(uname);
	return NULL;
}




/**
struct passwd* getnisnam(char* usrid)

	機能：NIS から usrid のパスワードを得る．

	引数：userid -- 調べるユーザ名

	戻り値：userid のパスワード構造体へのポインタ．要free
*/

struct passwd* getnisnam(char* usrid)
{
	struct passwd* pw;
	char*  buf;
	char*  nis;

	nis = get_nis_passwdf(usrid);
	if (nis==NULL) return NULL;
	pw = (struct passwd*)malloc(sizeof(struct passwd));
	if (pw==NULL) {
		free(nis);
		return NULL;
	}

	pw->pw_name   = awk(nis, ':', 1);
	pw->pw_passwd = awk(nis, ':', 2);
	pw->pw_gecos  = awk(nis, ':', 5);
	pw->pw_dir	  = awk(nis, ':', 6);
	pw->pw_shell  = awk(nis, ':', 7);

	buf = awk(nis, ':', 3);
	if (buf!=NULL) pw->pw_uid = atoi(buf);
	free(buf);

	buf = awk(nis, ':', 4);
	if (buf!=NULL) pw->pw_gid = atoi(buf);
	free(buf);

	free(nis);
	return pw;
}
		
#endif		// HAVE_RPCSVC_YPCLNT_H




/**
void  free_pw(struct passwd* pw)

	機能：パスワード構造体 struct pw* を free する．getnisnam()の返す構造体にのみ
　		　適用すること．一般のUNIXライブラリの返すパスワード構造体に対して適用して
		  はいけない（セグメンテーションフォルトを起こす）．
*/
void  free_pw(struct passwd* pw)
{
	if (pw==NULL) return;

	freeNull(pw->pw_name);
	freeNull(pw->pw_passwd);
	freeNull(pw->pw_gecos);
	freeNull(pw->pw_dir);
	freeNull(pw->pw_shell);
	freeNull(pw);
	return;
}







/**
char*   x2crypt(char* pass, char* bsalt)

	機能: pass を bsalt で２回暗号化する．bsaltには改行コード(\n, \r\n)で区切られた
　　　　　２個のsaltが入っている必要がある．


	要チェック：bsaltに２個のslatが入っていなかったときの動作は? -> 不明

*/
char*   x2crypt(char* pass, char* bsalt)
{
	char* cpass;
	char* dpass = NULL;
	char* csalt;

	if (pass==NULL || bsalt==NULL) return NULL;

	csalt = get_line(bsalt, 1);
	cpass = crypt(pass, csalt);
	freeNull(csalt);
	csalt = get_line(bsalt, 2);
	if (csalt==NULL) return NULL;

	if (strlen(cpass)==LEN_MD5PASS) {
		dpass = cut_str(cpass, LEN_MD5SALT, LEN_MD5PASS-1);
		cpass = crypt(dpass, csalt);
		freeNull(dpass);
		dpass = cut_str(cpass, LEN_MD5SALT, LEN_MD5PASS-1);
	}
	else if (strlen(cpass)==LEN_DESPASS) {
		dpass = cut_str(cpass, LEN_DESSALT, LEN_DESPASS-1);
		cpass = crypt(dpass, csalt);
		freeNull(dpass);
		dpass = cut_str(cpass, LEN_DESSALT, LEN_DESPASS-1);
	}

	freeNull(csalt);
	return dpass;
}



/**
int  check_passwd(char* passwd, char* cryptpass)

	機能：生パスワード passwd と暗号化されたパスワード cryptass(salt付き)が
		  同じものであるかどうかチェックする．
	      使用できる暗号化は DES, MD5 

	戻り値：TRUE  同じ
			FALSE 違う
	
*/
int  check_passwd(char* passwd, char* cryptpass)
{
	int  samepass=FALSE;
	char* salt;

	if (passwd==NULL || cryptpass==NULL) return FALSE;

	if (!strncmp("$1$", cryptpass, 3) && strlen(cryptpass)==LEN_MD5PASS) {
		// MD5
		salt = cut_str(cryptpass, 0, LEN_MD5SALT-1);
		if (!strcmp(crypt(passwd, salt), cryptpass)) {
			samepass = TRUE;
		}
		free(salt);
	}
	else if (strlen(cryptpass)==LEN_DESPASS) {
		// DES
		salt = cut_str(cryptpass, 0, LEN_DESSALT-1);
		if (!strcmp(crypt(passwd, salt), cryptpass)) {
			samepass = TRUE;
		}
		free(salt);
	}

	return samepass;
}



