diff --git a/.gitignore b/.gitignore index 4e0355d..2a3d2d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .*.sw? .lock-wscript +build diff --git a/CHANGELOG b/CHANGELOG index 7d2fb09..7e6be40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +0.2.0 (): + * Node.js v0.8 support + * async crypt + 0.1.3 (Tue Nov 8 14:50:59 MYT 2011): * Node.js v0.6 support, fixes issue #3 * Updated demos, fixed miscellaneos typos diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..24b948f --- /dev/null +++ b/binding.gyp @@ -0,0 +1,23 @@ +{ + "targets": [ + { + "target_name": "unixlib", + "include_dirs": [ "security/pam_appl.h","crypt.h" ], + "direct_dependent_settings": { + "linkflags": [ "-lpam", "-lcrypt" ] + }, + 'link_settings': { + 'libraries': ["-lpam", "-lcrypt"], + }, + + "conditions": [ + [ "OS=='win'", { + + }, { # OS != win + "cflags": [ "-g", "-D_FILE_OFFSET_BITS=64", "-D_LARGEFILE_SOURCE", "-Wall" ] + } + ] ], + "sources": [ "unixlib.cc" ] + } + ] +} \ No newline at end of file diff --git a/demo/flock.js b/demo/flock.js index 312ff89..ea94727 100644 --- a/demo/flock.js +++ b/demo/flock.js @@ -1,5 +1,5 @@ var fs = require("fs"); -var unixlib = require("../build/Release/unixlib"); +var unixlib = require("../build/Release/unixlib.node"); var filename = "/tmp/flock.example"; // Let's try flocking diff --git a/demo/mkstemp.js b/demo/mkstemp.js index dc7ff15..9ab0ac9 100644 --- a/demo/mkstemp.js +++ b/demo/mkstemp.js @@ -1,4 +1,4 @@ -var unixlib = require("../build/Release/unixlib"); +var unixlib = require("../build/Release/unixlib.node"); var goodstrtemplate = "/tmp/mkstempXXXXXX"; var badstrtemplate = "/tmp/mkstempXXX"; diff --git a/demo/pam.js b/demo/pam.js index 1307523..8f6aa8e 100644 --- a/demo/pam.js +++ b/demo/pam.js @@ -1,4 +1,4 @@ -var unixlib = require("../build/Release/unixlib"); +var unixlib = require("../build/Release/unixlib.node"); // Change accordingly or write your own. var service = "system-auth"; diff --git a/demo/testcrypt.js b/demo/testcrypt.js new file mode 100644 index 0000000..821cd68 --- /dev/null +++ b/demo/testcrypt.js @@ -0,0 +1,17 @@ +var unixlib = require("../build/Release/unixlib.node"); + + +var res = unixlib.crypt("testpasswd"); +console.log("without salt : ",res); + +res = unixlib.crypt("testpasswd","12"); +console.log("with salt : ",res); + +// async call has unpredictable results here +// TODO: fix bugs in cc impl +unixlib.cryptAsync("testpasswd","12",function(){ + console.log("with salt : ", arguments); + }); +unixlib.cryptAsync("testpasswd",function(){ + console.log("without salt : ",arguments); + }); \ No newline at end of file diff --git a/package.json b/package.json index 93f85f4..9d54e8f 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,20 @@ { "name" : "unixlib", - "version" : "v0.1.3", - "description" : "Native Linux utilities for Node.js, currently PAM authentication, flock() and mkstemp", + "version" : "v0.2.0", + "description" : "Native Linux utilities for Node.js, currently PAM authentication, flock(), mkstemp and crypt()", "homepage" : "https://github.com/ditesh/node-unixlib", - "keywords": ["linux", "flock", "pam", "authentication", "unix"], + "keywords": ["linux", "flock", "pam", "authentication", "unix","crypt"], "author" : { "name" : "Ditesh Shashikant Gathani", "email" : "ditesh@gathani.org", "url" : "http://ditesh.gathani.org/blog/" }, + "contributors": [ + { "name": "Nicolas Karageuzian", "email": "nico@karageuzian.com" } + ], "repository" : { "type" : "git", "url" : "https://github.com/ditesh/node-unixlib.git" }, "scripts" : { - "preinstall" : "node-waf configure && node-waf build", + "preinstall" : "node-gyp configure && node-gyp build", "preuninstall" : "rm -r build/*" }, "main" : "build/Release/unixlib.node" diff --git a/unixlib.cc b/unixlib.cc index a4a2c1c..014d42c 100644 --- a/unixlib.cc +++ b/unixlib.cc @@ -4,8 +4,13 @@ #include #include #include +#include #include +#ifndef NODE_MAX_SALT_LEN +#define NODE_MAX_SALT_LEN 2 +#endif + #define REQ_FUN_ARG(I, VAR) \ if (args.Length() <= (I) || !args[I]->IsFunction()) \ return ThrowException(Exception::TypeError( \ @@ -25,6 +30,10 @@ static int AfterFlock(eio_req *); static Handle PAMAuthAsync(const Arguments&); static void PAMAuth(eio_req *); static int AfterPAMAuth(eio_req *); +Handle CryptAsync(const Arguments&); +Handle CryptSync(const Arguments&); +void Crypt(eio_req *); +int AfterCrypt(eio_req *); extern "C" void init(Handle); extern "C" { @@ -85,6 +94,13 @@ struct pam_baton { const char *password; Persistent cb; }; +struct crypt_baton { + char *passwd; + char *salt; + char *result; + Persistent cb; +}; + static Handle MkstempAsync(const Arguments& args) { @@ -165,6 +181,80 @@ static Handle PAMAuthAsync(const Arguments& args) { } +Handle CryptSync(const Arguments& args) { + HandleScope scope; + const char *usage = "usage: cryptSync(password [, salt ])"; + char salt[NODE_MAX_SALT_LEN + 1]; + salt[0] = salt[NODE_MAX_SALT_LEN] = '\0'; + bool salt_def = false; + bool valid_call = false; + + strncpy(salt, "$1$", NODE_MAX_SALT_LEN); + if (args.Length() == 1) { + valid_call = true; + } + if (args.Length() == 2) { + valid_call = true; + salt_def = true; + } + if (!valid_call) + return ThrowException(Exception::Error(String::New(usage))); + +String::Utf8Value password(args[0]); + if (salt_def) { + String::Utf8Value salt_arg(args[1]); + strncpy(salt, ToCString(salt_arg), NODE_MAX_SALT_LEN); + } + char *result = crypt(ToCString(password),salt); + + return scope.Close(String::New(result)); +} + +Handle CryptAsync(const Arguments& args) { + + HandleScope scope; + const char *usage = "usage: crypt(password [, salt ], callback)"; + char salt[NODE_MAX_SALT_LEN + 1]; + //Local salt_def = Local::New(Boolean::New(false)); + bool salt_def = false; + bool valid_call = false; + int cbid = 2; + salt[0] = salt[NODE_MAX_SALT_LEN] = '\0'; + strncpy(salt, "$1$", NODE_MAX_SALT_LEN); + if (args.Length() == 2) { + valid_call = true; + cbid = 1; + } + if (args.Length() == 3) { + valid_call = true; + salt_def = true; + cbid = 2; + } + if (!valid_call) + return ThrowException(Exception::Error(String::New(usage))); + + REQ_FUN_ARG(cbid, cb); + + crypt_baton *baton = new crypt_baton(); + baton->result = false; + + String::Utf8Value password(args[0]); + if (salt_def) { + String::Utf8Value salt_arg(args[1]); + strncpy(salt, ToCString(salt_arg), NODE_MAX_SALT_LEN); + } + baton->salt = strdup(salt); + + baton->passwd = strdup(ToCString(password)); + + baton->cb = Persistent::New(cb); + + eio_custom(Crypt, EIO_PRI_DEFAULT, AfterCrypt, baton); + ev_ref(EV_DEFAULT_UC); + return scope.Close(Undefined()); + +} + static void Mkstemp(eio_req *req) { struct mkstemp_baton * baton = (struct mkstemp_baton *)req->data; @@ -203,6 +293,15 @@ static void PAMAuth(eio_req *req) { if (retval == PAM_SUCCESS) baton->result = true; +} +void Crypt(eio_req *req) { + + struct crypt_baton* baton = (struct crypt_baton*) req->data; + //struct crypt_data buffer; + char *passwd = strdup(baton->passwd); + char *salt = strdup(baton->salt); + baton->result = crypt(passwd, salt); + } static int AfterMkstemp(eio_req *req) { @@ -288,11 +387,41 @@ static int AfterPAMAuth(eio_req *req) { } +int AfterCrypt(eio_req *req) { + + HandleScope scope; + ev_unref(EV_DEFAULT_UC); + crypt_baton *baton = static_cast(req->data); + + Local argv[1]; + + if (baton->result) { + argv[0] = Local::New(True()); + argv[1] = String::New(baton->result); + } + else + argv[0] = Local::New(False()); + + TryCatch try_catch; + + baton->cb->Call(Context::GetCurrent()->Global(), 2, argv); + + if (try_catch.HasCaught()) + FatalException(try_catch); + + baton->cb.Dispose(); + delete baton; + return 0; + +} + extern "C" void init (Handle target) { HandleScope scope; NODE_SET_METHOD(target, "flock", FlockAsync); NODE_SET_METHOD(target, "pamauth", PAMAuthAsync); NODE_SET_METHOD(target, "mkstemp", MkstempAsync); + NODE_SET_METHOD(target, "cryptAsync", CryptAsync); + NODE_SET_METHOD(target, "crypt", CryptSync); } diff --git a/wscript b/wscript index fd91736..51652b9 100644 --- a/wscript +++ b/wscript @@ -1,6 +1,6 @@ srcdir = '.' blddir = 'build' -VERSION = '0.0.1' +VERSION = '0.2.0' def set_options(opt): opt.tool_options('compiler_cxx') @@ -9,7 +9,8 @@ def configure(conf): conf.check_tool('compiler_cxx') conf.check_tool('node_addon') conf.check(header_name="security/pam_appl.h", mandatory=True) - conf.env.LINKFLAGS = ["-lpam"] + conf.check(header_name="crypt.h", mandatory=True) + conf.env.LINKFLAGS = ["-lpam","-lcrypt"] def build(bld): obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')